summaryrefslogtreecommitdiff
path: root/thirdparty/lws/server
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/lws/server')
-rw-r--r--thirdparty/lws/server/fops-zip.c669
-rw-r--r--thirdparty/lws/server/lejp-conf.c929
-rw-r--r--thirdparty/lws/server/parsers.c1783
-rw-r--r--thirdparty/lws/server/ranges.c214
-rw-r--r--thirdparty/lws/server/server-handshake.c360
-rw-r--r--thirdparty/lws/server/server.c3025
-rw-r--r--thirdparty/lws/server/ssl-server.c477
7 files changed, 0 insertions, 7457 deletions
diff --git a/thirdparty/lws/server/fops-zip.c b/thirdparty/lws/server/fops-zip.c
deleted file mode 100644
index 2b254f67af..0000000000
--- a/thirdparty/lws/server/fops-zip.c
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Original code used in this source file:
- *
- * https://github.com/PerBothner/DomTerm.git @912add15f3d0aec
- *
- * ./lws-term/io.c
- * ./lws-term/junzip.c
- *
- * Copyright (C) 2017 Per Bothner <per@bothner.com>
- *
- * MIT License
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * ( copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- *
- * lws rewrite:
- *
- * Copyright (C) 2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-#include <zlib.h>
-
-/*
- * This code works with zip format containers which may have files compressed
- * with gzip deflate (type 8) or store uncompressed (type 0).
- *
- * Linux zip produces such zipfiles by default, eg
- *
- * $ zip ../myzip.zip file1 file2 file3
- */
-
-#define ZIP_COMPRESSION_METHOD_STORE 0
-#define ZIP_COMPRESSION_METHOD_DEFLATE 8
-
-typedef struct {
- lws_filepos_t filename_start;
- uint32_t crc32;
- uint32_t comp_size;
- uint32_t uncomp_size;
- uint32_t offset;
- uint32_t mod_time;
- uint16_t filename_len;
- uint16_t extra;
- uint16_t method;
- uint16_t file_com_len;
-} lws_fops_zip_hdr_t;
-
-typedef struct {
- struct lws_fop_fd fop_fd; /* MUST BE FIRST logical fop_fd into
- * file inside zip: fops_zip fops */
- lws_fop_fd_t zip_fop_fd; /* logical fop fd on to zip file
- * itself: using platform fops */
- lws_fops_zip_hdr_t hdr;
- z_stream inflate;
- lws_filepos_t content_start;
- lws_filepos_t exp_uncomp_pos;
- union {
- uint8_t trailer8[8];
- uint32_t trailer32[2];
- } u;
- uint8_t rbuf[128]; /* decompression chunk size */
- int entry_count;
-
- unsigned int decompress:1; /* 0 = direct from file */
- unsigned int add_gzip_container:1;
-} *lws_fops_zip_t;
-
-struct lws_plat_file_ops fops_zip;
-#define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD))
-
-static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 };
-
-enum {
- ZC_SIGNATURE = 0,
- ZC_VERSION_MADE_BY = 4,
- ZC_VERSION_NEEDED_TO_EXTRACT = 6,
- ZC_GENERAL_PURPOSE_BIT_FLAG = 8,
- ZC_COMPRESSION_METHOD = 10,
- ZC_LAST_MOD_FILE_TIME = 12,
- ZC_LAST_MOD_FILE_DATE = 14,
- ZC_CRC32 = 16,
- ZC_COMPRESSED_SIZE = 20,
- ZC_UNCOMPRESSED_SIZE = 24,
- ZC_FILE_NAME_LENGTH = 28,
- ZC_EXTRA_FIELD_LENGTH = 30,
-
- ZC_FILE_COMMENT_LENGTH = 32,
- ZC_DISK_NUMBER_START = 34,
- ZC_INTERNAL_FILE_ATTRIBUTES = 36,
- ZC_EXTERNAL_FILE_ATTRIBUTES = 38,
- ZC_REL_OFFSET_LOCAL_HEADER = 42,
- ZC_DIRECTORY_LENGTH = 46,
-
- ZE_SIGNATURE_OFFSET = 0,
- ZE_DESK_NUMBER = 4,
- ZE_CENTRAL_DIRECTORY_DISK_NUMBER = 6,
- ZE_NUM_ENTRIES_THIS_DISK = 8,
- ZE_NUM_ENTRIES = 10,
- ZE_CENTRAL_DIRECTORY_SIZE = 12,
- ZE_CENTRAL_DIR_OFFSET = 16,
- ZE_ZIP_COMMENT_LENGTH = 20,
- ZE_DIRECTORY_LENGTH = 22,
-
- ZL_REL_OFFSET_CONTENT = 28,
- ZL_HEADER_LENGTH = 30,
-
- LWS_FZ_ERR_SEEK_END_RECORD = 1,
- LWS_FZ_ERR_READ_END_RECORD,
- LWS_FZ_ERR_END_RECORD_MAGIC,
- LWS_FZ_ERR_END_RECORD_SANITY,
- LWS_FZ_ERR_CENTRAL_SEEK,
- LWS_FZ_ERR_CENTRAL_READ,
- LWS_FZ_ERR_CENTRAL_SANITY,
- LWS_FZ_ERR_NAME_TOO_LONG,
- LWS_FZ_ERR_NAME_SEEK,
- LWS_FZ_ERR_NAME_READ,
- LWS_FZ_ERR_CONTENT_SANITY,
- LWS_FZ_ERR_CONTENT_SEEK,
- LWS_FZ_ERR_SCAN_SEEK,
- LWS_FZ_ERR_NOT_FOUND,
- LWS_FZ_ERR_ZLIB_INIT,
- LWS_FZ_ERR_READ_CONTENT,
- LWS_FZ_ERR_SEEK_COMPRESSED,
-};
-
-static uint16_t
-get_u16(void *p)
-{
- const uint8_t *c = (const uint8_t *)p;
-
- return (uint16_t)((c[0] | (c[1] << 8)));
-}
-
-static uint32_t
-get_u32(void *p)
-{
- const uint8_t *c = (const uint8_t *)p;
-
- return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
-}
-
-int
-lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
-{
- lws_filepos_t amount;
- uint8_t buf[96];
- int i;
-
- if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0)
- return LWS_FZ_ERR_SEEK_END_RECORD;
-
- if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
- ZE_DIRECTORY_LENGTH))
- return LWS_FZ_ERR_READ_END_RECORD;
-
- if (amount != ZE_DIRECTORY_LENGTH)
- return LWS_FZ_ERR_READ_END_RECORD;
-
- /*
- * We require the zip to have the last record right at the end
- * Linux zip always does this if no zip comment.
- */
- if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6)
- return LWS_FZ_ERR_END_RECORD_MAGIC;
-
- i = get_u16(buf + ZE_NUM_ENTRIES);
-
- if (get_u16(buf + ZE_DESK_NUMBER) ||
- get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
- i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK))
- return LWS_FZ_ERR_END_RECORD_SANITY;
-
- /* end record is OK... look for our file in the central dir */
-
- if (lws_vfs_file_seek_set(priv->zip_fop_fd,
- get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0)
- return LWS_FZ_ERR_CENTRAL_SEEK;
-
- while (i--) {
- priv->content_start = lws_vfs_tell(priv->zip_fop_fd);
-
- if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
- ZC_DIRECTORY_LENGTH))
- return LWS_FZ_ERR_CENTRAL_READ;
-
- if (amount != ZC_DIRECTORY_LENGTH)
- return LWS_FZ_ERR_CENTRAL_READ;
-
- if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50)
- return LWS_FZ_ERR_CENTRAL_SANITY;
-
- lwsl_debug("cstart 0x%lx\n", (unsigned long)priv->content_start);
-
- priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH);
- priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH);
- priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd);
-
- priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD);
- priv->hdr.crc32 = get_u32(buf + ZC_CRC32);
- priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE);
- priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE);
- priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER);
- priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME);
- priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH);
-
- if (priv->hdr.filename_len != len)
- goto next;
-
- if (len >= sizeof(buf) - 1)
- return LWS_FZ_ERR_NAME_TOO_LONG;
-
- if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
- &amount, buf, len))
- return LWS_FZ_ERR_NAME_READ;
- if (amount != len)
- return LWS_FZ_ERR_NAME_READ;
-
- buf[len] = '\0';
- lwsl_debug("check %s vs %s\n", buf, name);
-
- if (strcmp((const char *)buf, name))
- goto next;
-
- /* we found a match */
- if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset) < 0)
- return LWS_FZ_ERR_NAME_SEEK;
- if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
- &amount, buf,
- ZL_HEADER_LENGTH))
- return LWS_FZ_ERR_NAME_READ;
- if (amount != ZL_HEADER_LENGTH)
- return LWS_FZ_ERR_NAME_READ;
-
- priv->content_start = priv->hdr.offset +
- ZL_HEADER_LENGTH +
- priv->hdr.filename_len +
- get_u16(buf + ZL_REL_OFFSET_CONTENT);
-
- lwsl_debug("content supposed to start at 0x%lx\n",
- (unsigned long)priv->content_start);
-
- if (priv->content_start > priv->zip_fop_fd->len)
- return LWS_FZ_ERR_CONTENT_SANITY;
-
- if (lws_vfs_file_seek_set(priv->zip_fop_fd,
- priv->content_start) < 0)
- return LWS_FZ_ERR_CONTENT_SEEK;
-
- /* we are aligned at the start of the content */
-
- priv->exp_uncomp_pos = 0;
-
- return 0;
-
-next:
- if (i && lws_vfs_file_seek_set(priv->zip_fop_fd,
- priv->content_start +
- ZC_DIRECTORY_LENGTH +
- priv->hdr.filename_len +
- priv->hdr.extra +
- priv->hdr.file_com_len) < 0)
- return LWS_FZ_ERR_SCAN_SEEK;
- }
-
- return LWS_FZ_ERR_NOT_FOUND;
-}
-
-static int
-lws_fops_zip_reset_inflate(lws_fops_zip_t priv)
-{
- if (priv->decompress)
- inflateEnd(&priv->inflate);
-
- priv->inflate.zalloc = Z_NULL;
- priv->inflate.zfree = Z_NULL;
- priv->inflate.opaque = Z_NULL;
- priv->inflate.avail_in = 0;
- priv->inflate.next_in = Z_NULL;
-
- if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) {
- lwsl_err("inflate init failed\n");
- return LWS_FZ_ERR_ZLIB_INIT;
- }
-
- if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->content_start) < 0)
- return LWS_FZ_ERR_CONTENT_SEEK;
-
- priv->exp_uncomp_pos = 0;
-
- return 0;
-}
-
-static lws_fop_fd_t
-lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
- const char *vpath, lws_fop_flags_t *flags)
-{
- lws_fop_flags_t local_flags = 0;
- lws_fops_zip_t priv;
- char rp[192];
- int m;
-
- /*
- * vpath points at the / after the fops signature in vfs_path, eg
- * with a vfs_path "/var/www/docs/manual.zip/index.html", vpath
- * will come pointing at "/index.html"
- */
-
- priv = lws_zalloc(sizeof(*priv), "fops_zip priv");
- if (!priv)
- return NULL;
-
- priv->fop_fd.fops = &fops_zip;
-
- m = sizeof(rp) - 1;
- if ((vpath - vfs_path - 1) < m)
- m = vpath - vfs_path - 1;
- strncpy(rp, vfs_path, m);
- rp[m] = '\0';
-
- /* open the zip file itself using the incoming fops, not fops_zip */
-
- priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags);
- if (!priv->zip_fop_fd) {
- lwsl_err("unable to open zip %s\n", rp);
- goto bail1;
- }
-
- if (*vpath == '/')
- vpath++;
-
- m = lws_fops_zip_scan(priv, vpath, strlen(vpath));
- if (m) {
- lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
- goto bail2;
- }
-
- /* the directory metadata tells us modification time, so pass it on */
- priv->fop_fd.mod_time = priv->hdr.mod_time;
- *flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
- priv->fop_fd.flags = *flags;
-
- /* The zip fop_fd is left pointing at the start of the content.
- *
- * 1) Content could be uncompressed (STORE), and we can always serve
- * that directly
- *
- * 2) Content could be compressed (GZIP), and the client can handle
- * receiving GZIP... we can wrap it in a GZIP header and trailer
- * and serve the content part directly. The flag indicating we
- * are providing GZIP directly is set so lws will send the right
- * headers.
- *
- * 3) Content could be compressed (GZIP) but the client can't handle
- * receiving GZIP... we can decompress it and serve as it is
- * inflated piecemeal.
- *
- * 4) Content may be compressed some unknown way... fail
- *
- */
- if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
- /*
- * it is stored uncompressed, leave it indicated as
- * uncompressed, and just serve it from inside the
- * zip with no gzip container;
- */
-
- lwsl_info("direct zip serving (stored)\n");
-
- priv->fop_fd.len = priv->hdr.uncomp_size;
-
- return &priv->fop_fd;
- }
-
- if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
- priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
-
- /*
- * We can serve the gzipped file contents directly as gzip
- * from inside the zip container; client says it is OK.
- *
- * To convert to standalone gzip, we have to add a 10-byte
- * constant header and a variable 8-byte trailer around the
- * content.
- *
- * The 8-byte trailer is prepared now and held in the priv.
- */
-
- lwsl_info("direct zip serving (gzipped)\n");
-
- priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
- sizeof(priv->u);
-
- if (lws_is_be()) {
- uint8_t *p = priv->u.trailer8;
-
- *p++ = (uint8_t)priv->hdr.crc32;
- *p++ = (uint8_t)(priv->hdr.crc32 >> 8);
- *p++ = (uint8_t)(priv->hdr.crc32 >> 16);
- *p++ = (uint8_t)(priv->hdr.crc32 >> 24);
- *p++ = (uint8_t)priv->hdr.uncomp_size;
- *p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
- *p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
- *p = (uint8_t)(priv->hdr.uncomp_size >> 24);
- } else {
- priv->u.trailer32[0] = priv->hdr.crc32;
- priv->u.trailer32[1] = priv->hdr.uncomp_size;
- }
-
- *flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
- priv->fop_fd.flags = *flags;
- priv->add_gzip_container = 1;
-
- return &priv->fop_fd;
- }
-
- if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
-
- /* we must decompress it to serve it */
-
- lwsl_info("decompressed zip serving\n");
-
- priv->fop_fd.len = priv->hdr.uncomp_size;
-
- if (lws_fops_zip_reset_inflate(priv)) {
- lwsl_err("inflate init failed\n");
- goto bail2;
- }
-
- priv->decompress = 1;
-
- return &priv->fop_fd;
- }
-
- /* we can't handle it ... */
-
- lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
- priv->hdr.method);
-
-bail2:
- lws_vfs_file_close(&priv->zip_fop_fd);
-bail1:
- free(priv);
-
- return NULL;
-}
-
-/* ie, we are closing the fop_fd for the file inside the gzip */
-
-static int
-lws_fops_zip_close(lws_fop_fd_t *fd)
-{
- lws_fops_zip_t priv = fop_fd_to_priv(*fd);
-
- if (priv->decompress)
- inflateEnd(&priv->inflate);
-
- lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
-
- free(priv);
- *fd = NULL;
-
- return 0;
-}
-
-static lws_fileofs_t
-lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
-{
- fd->pos += offset_from_cur_pos;
-
- return fd->pos;
-}
-
-static int
-lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
- lws_filepos_t len)
-{
- lws_fops_zip_t priv = fop_fd_to_priv(fd);
- lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
- int ret;
-
- if (priv->decompress) {
-
- if (priv->exp_uncomp_pos != fd->pos) {
- /*
- * there has been a seek in the uncompressed fop_fd
- * we have to restart the decompression and loop eating
- * the decompressed data up to the seek point
- */
- lwsl_info("seek in decompressed\n");
-
- lws_fops_zip_reset_inflate(priv);
-
- while (priv->exp_uncomp_pos != fd->pos) {
- rlen = len;
- if (rlen > fd->pos - priv->exp_uncomp_pos)
- rlen = fd->pos - priv->exp_uncomp_pos;
- if (lws_fops_zip_read(fd, amount, buf, rlen))
- return LWS_FZ_ERR_SEEK_COMPRESSED;
- }
- *amount = 0;
- }
-
- priv->inflate.avail_out = (unsigned int)len;
- priv->inflate.next_out = buf;
-
-spin:
- if (!priv->inflate.avail_in) {
- rlen = sizeof(priv->rbuf);
- if (rlen > priv->hdr.comp_size -
- (cur - priv->content_start))
- rlen = priv->hdr.comp_size -
- (priv->hdr.comp_size -
- priv->content_start);
-
- if (priv->zip_fop_fd->fops->LWS_FOP_READ(
- priv->zip_fop_fd, &ramount, priv->rbuf,
- rlen))
- return LWS_FZ_ERR_READ_CONTENT;
-
- cur += ramount;
-
- priv->inflate.avail_in = (unsigned int)ramount;
- priv->inflate.next_in = priv->rbuf;
- }
-
- ret = inflate(&priv->inflate, Z_NO_FLUSH);
- if (ret == Z_STREAM_ERROR)
- return ret;
-
- switch (ret) {
- case Z_NEED_DICT:
- ret = Z_DATA_ERROR;
- /* and fall through */
- case Z_DATA_ERROR:
- case Z_MEM_ERROR:
-
- return ret;
- }
-
- if (!priv->inflate.avail_in && priv->inflate.avail_out &&
- cur != priv->content_start + priv->hdr.comp_size)
- goto spin;
-
- *amount = len - priv->inflate.avail_out;
-
- priv->exp_uncomp_pos += *amount;
- fd->pos += *amount;
-
- return 0;
- }
-
- if (priv->add_gzip_container) {
-
- lwsl_info("%s: gzip + container\n", __func__);
- *amount = 0;
-
- /* place the canned header at the start */
-
- if (len && fd->pos < sizeof(hd)) {
- rlen = sizeof(hd) - fd->pos;
- if (rlen > len)
- rlen = len;
- /* provide stuff from canned header */
- memcpy(buf, hd + fd->pos, (size_t)rlen);
- fd->pos += rlen;
- buf += rlen;
- len -= rlen;
- *amount += rlen;
- }
-
- /* serve gzipped data direct from zipfile */
-
- if (len && fd->pos >= sizeof(hd) &&
- fd->pos < priv->hdr.comp_size + sizeof(hd)) {
-
- rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos -
- priv->content_start);
- if (rlen > len)
- rlen = len;
-
- if (rlen &&
- priv->zip_fop_fd->pos < (priv->hdr.comp_size +
- priv->content_start)) {
- if (lws_vfs_file_read(priv->zip_fop_fd,
- &ramount, buf, rlen))
- return LWS_FZ_ERR_READ_CONTENT;
- *amount += ramount;
- fd->pos += ramount; // virtual pos
- buf += ramount;
- len -= ramount;
- }
- }
-
- /* place the prepared trailer at the end */
-
- if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) &&
- fd->pos < priv->hdr.comp_size + sizeof(hd) +
- sizeof(priv->u)) {
- cur = fd->pos - priv->hdr.comp_size - sizeof(hd);
- rlen = sizeof(priv->u) - cur;
- if (rlen > len)
- rlen = len;
-
- memcpy(buf, priv->u.trailer8 + cur, (size_t)rlen);
-
- *amount += rlen;
- fd->pos += rlen;
- }
-
- return 0;
- }
-
- lwsl_info("%s: store\n", __func__);
-
- if (len > priv->hdr.uncomp_size - (cur - priv->content_start))
- len = priv->hdr.comp_size - (priv->hdr.comp_size -
- priv->content_start);
-
- if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
- amount, buf, len))
- return LWS_FZ_ERR_READ_CONTENT;
-
- return 0;
-}
-
-struct lws_plat_file_ops fops_zip = {
- lws_fops_zip_open,
- lws_fops_zip_close,
- lws_fops_zip_seek_cur,
- lws_fops_zip_read,
- NULL,
- { { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
- NULL,
-};
diff --git a/thirdparty/lws/server/lejp-conf.c b/thirdparty/lws/server/lejp-conf.c
deleted file mode 100644
index c2b684c278..0000000000
--- a/thirdparty/lws/server/lejp-conf.c
+++ /dev/null
@@ -1,929 +0,0 @@
-/*
- * libwebsockets web server application
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-#include "../misc/lejp.h"
-
-#ifndef _WIN32
-/* this is needed for Travis CI */
-#include <dirent.h>
-#endif
-
-#define ESC_INSTALL_DATADIR "_lws_ddir_"
-
-static const char * const paths_global[] = {
- "global.uid",
- "global.gid",
- "global.count-threads",
- "global.init-ssl",
- "global.server-string",
- "global.plugin-dir",
- "global.ws-pingpong-secs",
- "global.timeout-secs",
- "global.reject-service-keywords[].*",
- "global.reject-service-keywords[]",
-};
-
-enum lejp_global_paths {
- LEJPGP_UID,
- LEJPGP_GID,
- LEJPGP_COUNT_THREADS,
- LWJPGP_INIT_SSL,
- LEJPGP_SERVER_STRING,
- LEJPGP_PLUGIN_DIR,
- LWJPGP_PINGPONG_SECS,
- LWJPGP_TIMEOUT_SECS,
- LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
- LWJPGP_REJECT_SERVICE_KEYWORDS
-};
-
-static const char * const paths_vhosts[] = {
- "vhosts[]",
- "vhosts[].mounts[]",
- "vhosts[].name",
- "vhosts[].port",
- "vhosts[].interface",
- "vhosts[].unix-socket",
- "vhosts[].sts",
- "vhosts[].host-ssl-key",
- "vhosts[].host-ssl-cert",
- "vhosts[].host-ssl-ca",
- "vhosts[].access-log",
- "vhosts[].mounts[].mountpoint",
- "vhosts[].mounts[].origin",
- "vhosts[].mounts[].protocol",
- "vhosts[].mounts[].default",
- "vhosts[].mounts[].auth-mask",
- "vhosts[].mounts[].cgi-timeout",
- "vhosts[].mounts[].cgi-env[].*",
- "vhosts[].mounts[].cache-max-age",
- "vhosts[].mounts[].cache-reuse",
- "vhosts[].mounts[].cache-revalidate",
- "vhosts[].mounts[].basic-auth",
- "vhosts[].mounts[].cache-intermediaries",
- "vhosts[].mounts[].extra-mimetypes.*",
- "vhosts[].mounts[].interpret.*",
- "vhosts[].ws-protocols[].*.*",
- "vhosts[].ws-protocols[].*",
- "vhosts[].ws-protocols[]",
- "vhosts[].keepalive_timeout",
- "vhosts[].enable-client-ssl",
- "vhosts[].ciphers",
- "vhosts[].ecdh-curve",
- "vhosts[].noipv6",
- "vhosts[].ipv6only",
- "vhosts[].ssl-option-set",
- "vhosts[].ssl-option-clear",
- "vhosts[].mounts[].pmo[].*",
- "vhosts[].headers[].*",
- "vhosts[].headers[]",
- "vhosts[].client-ssl-key",
- "vhosts[].client-ssl-cert",
- "vhosts[].client-ssl-ca",
- "vhosts[].client-ssl-ciphers",
- "vhosts[].onlyraw",
-};
-
-enum lejp_vhost_paths {
- LEJPVP,
- LEJPVP_MOUNTS,
- LEJPVP_NAME,
- LEJPVP_PORT,
- LEJPVP_INTERFACE,
- LEJPVP_UNIXSKT,
- LEJPVP_STS,
- LEJPVP_HOST_SSL_KEY,
- LEJPVP_HOST_SSL_CERT,
- LEJPVP_HOST_SSL_CA,
- LEJPVP_ACCESS_LOG,
- LEJPVP_MOUNTPOINT,
- LEJPVP_ORIGIN,
- LEJPVP_MOUNT_PROTOCOL,
- LEJPVP_DEFAULT,
- LEJPVP_DEFAULT_AUTH_MASK,
- LEJPVP_CGI_TIMEOUT,
- LEJPVP_CGI_ENV,
- LEJPVP_MOUNT_CACHE_MAX_AGE,
- LEJPVP_MOUNT_CACHE_REUSE,
- LEJPVP_MOUNT_CACHE_REVALIDATE,
- LEJPVP_MOUNT_BASIC_AUTH,
- LEJPVP_MOUNT_CACHE_INTERMEDIARIES,
- LEJPVP_MOUNT_EXTRA_MIMETYPES,
- LEJPVP_MOUNT_INTERPRET,
- LEJPVP_PROTOCOL_NAME_OPT,
- LEJPVP_PROTOCOL_NAME,
- LEJPVP_PROTOCOL,
- LEJPVP_KEEPALIVE_TIMEOUT,
- LEJPVP_ENABLE_CLIENT_SSL,
- LEJPVP_CIPHERS,
- LEJPVP_ECDH_CURVE,
- LEJPVP_NOIPV6,
- LEJPVP_IPV6ONLY,
- LEJPVP_SSL_OPTION_SET,
- LEJPVP_SSL_OPTION_CLEAR,
- LEJPVP_PMO,
- LEJPVP_HEADERS_NAME,
- LEJPVP_HEADERS,
- LEJPVP_CLIENT_SSL_KEY,
- LEJPVP_CLIENT_SSL_CERT,
- LEJPVP_CLIENT_SSL_CA,
- LEJPVP_CLIENT_CIPHERS,
- LEJPVP_FLAG_ONLYRAW,
-};
-
-static const char * const parser_errs[] = {
- "",
- "",
- "No opening '{'",
- "Expected closing '}'",
- "Expected '\"'",
- "String underrun",
- "Illegal unescaped control char",
- "Illegal escape format",
- "Illegal hex number",
- "Expected ':'",
- "Illegal value start",
- "Digit required after decimal point",
- "Bad number format",
- "Bad exponent format",
- "Unknown token",
- "Too many ']'",
- "Mismatched ']'",
- "Expected ']'",
- "JSON nesting limit exceeded",
- "Nesting tracking used up",
- "Number too long",
- "Comma or block end expected",
- "Unknown",
- "Parser callback errored (see earlier error)",
-};
-
-#define MAX_PLUGIN_DIRS 10
-
-struct jpargs {
- struct lws_context_creation_info *info;
- struct lws_context *context;
- const struct lws_protocols *protocols;
- const struct lws_extension *extensions;
- char *p, *end, valid;
- struct lws_http_mount *head, *last;
-
- struct lws_protocol_vhost_options *pvo;
- struct lws_protocol_vhost_options *pvo_em;
- struct lws_protocol_vhost_options *pvo_int;
- struct lws_http_mount m;
- const char **plugin_dirs;
- int count_plugin_dirs;
-
- unsigned int enable_client_ssl:1;
- unsigned int fresh_mount:1;
- unsigned int any_vhosts:1;
-};
-
-static void *
-lwsws_align(struct jpargs *a)
-{
- if ((lws_intptr_t)(a->p) & 15)
- a->p += 16 - ((lws_intptr_t)(a->p) & 15);
-
- return a->p;
-}
-
-static int
-arg_to_bool(const char *s)
-{
- static const char * const on[] = { "on", "yes", "true" };
- int n = atoi(s);
-
- if (n)
- return 1;
-
- for (n = 0; n < ARRAY_SIZE(on); n++)
- if (!strcasecmp(s, on[n]))
- return 1;
-
- return 0;
-}
-
-static signed char
-lejp_globals_cb(struct lejp_ctx *ctx, char reason)
-{
- struct jpargs *a = (struct jpargs *)ctx->user;
- struct lws_protocol_vhost_options *rej;
- int n;
-
- /* we only match on the prepared path strings */
- if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
- return 0;
-
- /* this catches, eg, vhosts[].headers[].xxx */
- if (reason == LEJPCB_VAL_STR_END &&
- ctx->path_match == LWJPGP_REJECT_SERVICE_KEYWORDS_NAME + 1) {
- rej = lwsws_align(a);
- a->p += sizeof(*rej);
-
- n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
- rej->next = a->info->reject_service_keywords;
- a->info->reject_service_keywords = rej;
- rej->name = a->p;
- lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf);
- a->p += n - 1;
- *(a->p++) = '\0';
- rej->value = a->p;
- rej->options = NULL;
- goto dostring;
- }
-
- switch (ctx->path_match - 1) {
- case LEJPGP_UID:
- a->info->uid = atoi(ctx->buf);
- return 0;
- case LEJPGP_GID:
- a->info->gid = atoi(ctx->buf);
- return 0;
- case LEJPGP_COUNT_THREADS:
- a->info->count_threads = atoi(ctx->buf);
- return 0;
- case LWJPGP_INIT_SSL:
- if (arg_to_bool(ctx->buf))
- a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
- return 0;
- case LEJPGP_SERVER_STRING:
- a->info->server_string = a->p;
- break;
- case LEJPGP_PLUGIN_DIR:
- if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) {
- lwsl_err("Too many plugin dirs\n");
- return -1;
- }
- a->plugin_dirs[a->count_plugin_dirs++] = a->p;
- break;
-
- case LWJPGP_PINGPONG_SECS:
- a->info->ws_ping_pong_interval = atoi(ctx->buf);
- return 0;
-
- case LWJPGP_TIMEOUT_SECS:
- a->info->timeout_secs = atoi(ctx->buf);
- return 0;
-
- default:
- return 0;
- }
-
-dostring:
- a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
- *(a->p)++ = '\0';
-
- return 0;
-}
-
-static signed char
-lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
-{
- struct jpargs *a = (struct jpargs *)ctx->user;
- struct lws_protocol_vhost_options *pvo, *mp_cgienv, *headers;
- struct lws_http_mount *m;
- char *p, *p1;
- int n;
-
-#if 0
- lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
- for (n = 0; n < ctx->wildcount; n++)
- lwsl_notice(" %d\n", ctx->wild[n]);
-#endif
-
- if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
- /* set the defaults for this vhost */
- a->valid = 1;
- a->head = NULL;
- a->last = NULL;
- a->info->port = 0;
- a->info->iface = NULL;
- a->info->protocols = a->protocols;
- a->info->extensions = a->extensions;
- a->info->ssl_cert_filepath = NULL;
- a->info->ssl_private_key_filepath = NULL;
- a->info->ssl_ca_filepath = NULL;
- a->info->client_ssl_cert_filepath = NULL;
- a->info->client_ssl_private_key_filepath = NULL;
- a->info->client_ssl_ca_filepath = NULL;
- a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
- "ECDHE-RSA-AES256-GCM-SHA384:"
- "DHE-RSA-AES256-GCM-SHA384:"
- "ECDHE-RSA-AES256-SHA384:"
- "HIGH:!aNULL:!eNULL:!EXPORT:"
- "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
- "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
- "!DHE-RSA-AES128-SHA256:"
- "!AES128-GCM-SHA256:"
- "!AES128-SHA256:"
- "!DHE-RSA-AES256-SHA256:"
- "!AES256-GCM-SHA384:"
- "!AES256-SHA256";
- a->info->timeout_secs = 5;
- a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
- "ECDHE-RSA-AES256-GCM-SHA384:"
- "DHE-RSA-AES256-GCM-SHA384:"
- "ECDHE-RSA-AES256-SHA384:"
- "HIGH:!aNULL:!eNULL:!EXPORT:"
- "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
- "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
- "!DHE-RSA-AES128-SHA256:"
- "!AES128-GCM-SHA256:"
- "!AES128-SHA256:"
- "!DHE-RSA-AES256-SHA256:"
- "!AES256-GCM-SHA384:"
- "!AES256-SHA256";
- a->info->pvo = NULL;
- a->info->headers = NULL;
- a->info->keepalive_timeout = 5;
- a->info->log_filepath = NULL;
- a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK |
- LWS_SERVER_OPTION_STS | LWS_SERVER_OPTION_ONLY_RAW);
- a->enable_client_ssl = 0;
- }
-
- if (reason == LEJPCB_OBJECT_START &&
- ctx->path_match == LEJPVP_MOUNTS + 1) {
- a->fresh_mount = 1;
- memset(&a->m, 0, sizeof(a->m));
- }
-
- /* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
- if (reason == LEJPCB_OBJECT_START &&
- ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
- a->pvo = lwsws_align(a);
- a->p += sizeof(*a->pvo);
-
- n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
- /* ie, enable this protocol, no options yet */
- a->pvo->next = a->info->pvo;
- a->info->pvo = a->pvo;
- a->pvo->name = a->p;
- lwsl_notice(" adding protocol %s\n", a->p);
- a->p += n;
- a->pvo->value = a->p;
- a->pvo->options = NULL;
- goto dostring;
- }
-
- /* this catches, eg, vhosts[].headers[].xxx */
- if (reason == LEJPCB_VAL_STR_END &&
- ctx->path_match == LEJPVP_HEADERS_NAME + 1) {
- headers = lwsws_align(a);
- a->p += sizeof(*headers);
-
- n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
- /* ie, enable this protocol, no options yet */
- headers->next = a->info->headers;
- a->info->headers = headers;
- headers->name = a->p;
- // lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf);
- a->p += n - 1;
- *(a->p++) = ':';
- if (a->p < a->end)
- *(a->p++) = '\0';
- else
- *(a->p - 1) = '\0';
- headers->value = a->p;
- headers->options = NULL;
- goto dostring;
- }
-
- if (reason == LEJPCB_OBJECT_END &&
- (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
- a->valid) {
-
- struct lws_vhost *vhost;
-
- //lwsl_notice("%s\n", ctx->path);
- if (!a->info->port) {
- lwsl_err("Port required (eg, 443)");
- return 1;
- }
- a->valid = 0;
- a->info->mounts = a->head;
-
- vhost = lws_create_vhost(a->context, a->info);
- if (!vhost) {
- lwsl_err("Failed to create vhost %s\n",
- a->info->vhost_name);
- return 1;
- }
- a->any_vhosts = 1;
-
- if (a->enable_client_ssl) {
- const char *cert_filepath = a->info->client_ssl_cert_filepath;
- const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
- const char *ca_filepath = a->info->client_ssl_ca_filepath;
- const char *cipher_list = a->info->client_ssl_cipher_list;
- memset(a->info, 0, sizeof(*a->info));
- a->info->client_ssl_cert_filepath = cert_filepath;
- a->info->client_ssl_private_key_filepath = private_key_filepath;
- a->info->client_ssl_ca_filepath = ca_filepath;
- a->info->client_ssl_cipher_list = cipher_list;
- a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
- lws_init_vhost_client_ssl(a->info, vhost);
- }
-
- return 0;
- }
-
- if (reason == LEJPCB_OBJECT_END &&
- ctx->path_match == LEJPVP_MOUNTS + 1) {
- static const char * const mount_protocols[] = {
- "http://",
- "https://",
- "file://",
- "cgi://",
- ">http://",
- ">https://",
- "callback://",
- "gzip://",
- };
-
- if (!a->fresh_mount)
- return 0;
-
- if (!a->m.mountpoint || !a->m.origin) {
- lwsl_err("mountpoint and origin required\n");
- return 1;
- }
- lwsl_debug("adding mount %s\n", a->m.mountpoint);
- m = lwsws_align(a);
- memcpy(m, &a->m, sizeof(*m));
- if (a->last)
- a->last->mount_next = m;
-
- for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
- if (!strncmp(a->m.origin, mount_protocols[n],
- strlen(mount_protocols[n]))) {
- lwsl_info("----%s\n", a->m.origin);
- m->origin_protocol = n;
- m->origin = a->m.origin +
- strlen(mount_protocols[n]);
- break;
- }
-
- if (n == ARRAY_SIZE(mount_protocols)) {
- lwsl_err("unsupported protocol:// %s\n", a->m.origin);
- return 1;
- }
-
- a->p += sizeof(*m);
- if (!a->head)
- a->head = m;
-
- a->last = m;
- a->fresh_mount = 0;
- }
-
- /* we only match on the prepared path strings */
- if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
- return 0;
-
- switch (ctx->path_match - 1) {
- case LEJPVP_NAME:
- a->info->vhost_name = a->p;
- break;
- case LEJPVP_PORT:
- a->info->port = atoi(ctx->buf);
- return 0;
- case LEJPVP_INTERFACE:
- a->info->iface = a->p;
- break;
- case LEJPVP_UNIXSKT:
- if (arg_to_bool(ctx->buf))
- a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK;
- else
- a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK);
- return 0;
- case LEJPVP_STS:
- if (arg_to_bool(ctx->buf))
- a->info->options |= LWS_SERVER_OPTION_STS;
- else
- a->info->options &= ~(LWS_SERVER_OPTION_STS);
- return 0;
- case LEJPVP_HOST_SSL_KEY:
- a->info->ssl_private_key_filepath = a->p;
- break;
- case LEJPVP_HOST_SSL_CERT:
- a->info->ssl_cert_filepath = a->p;
- break;
- case LEJPVP_HOST_SSL_CA:
- a->info->ssl_ca_filepath = a->p;
- break;
- case LEJPVP_ACCESS_LOG:
- a->info->log_filepath = a->p;
- break;
- case LEJPVP_MOUNTPOINT:
- a->m.mountpoint = a->p;
- a->m.mountpoint_len = (unsigned char)strlen(ctx->buf);
- break;
- case LEJPVP_ORIGIN:
- if (!strncmp(ctx->buf, "callback://", 11))
- a->m.protocol = a->p + 11;
-
- if (!a->m.origin)
- a->m.origin = a->p;
- break;
- case LEJPVP_DEFAULT:
- a->m.def = a->p;
- break;
- case LEJPVP_DEFAULT_AUTH_MASK:
- a->m.auth_mask = atoi(ctx->buf);
- return 0;
- case LEJPVP_MOUNT_CACHE_MAX_AGE:
- a->m.cache_max_age = atoi(ctx->buf);
- return 0;
- case LEJPVP_MOUNT_CACHE_REUSE:
- a->m.cache_reusable = arg_to_bool(ctx->buf);
- return 0;
- case LEJPVP_MOUNT_CACHE_REVALIDATE:
- a->m.cache_revalidate = arg_to_bool(ctx->buf);
- return 0;
- case LEJPVP_MOUNT_CACHE_INTERMEDIARIES:
- a->m.cache_intermediaries = arg_to_bool(ctx->buf);;
- return 0;
- case LEJPVP_MOUNT_BASIC_AUTH:
- a->m.basic_auth_login_file = a->p;
- break;
- case LEJPVP_CGI_TIMEOUT:
- a->m.cgi_timeout = atoi(ctx->buf);
- return 0;
- case LEJPVP_KEEPALIVE_TIMEOUT:
- a->info->keepalive_timeout = atoi(ctx->buf);
- return 0;
- case LEJPVP_CLIENT_CIPHERS:
- a->info->client_ssl_cipher_list = a->p;
- break;
- case LEJPVP_CIPHERS:
- a->info->ssl_cipher_list = a->p;
- break;
- case LEJPVP_ECDH_CURVE:
- a->info->ecdh_curve = a->p;
- break;
- case LEJPVP_PMO:
- case LEJPVP_CGI_ENV:
- mp_cgienv = lwsws_align(a);
- a->p += sizeof(*a->m.cgienv);
-
- mp_cgienv->next = a->m.cgienv;
- a->m.cgienv = mp_cgienv;
-
- n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
- mp_cgienv->name = a->p;
- a->p += n;
- mp_cgienv->value = a->p;
- mp_cgienv->options = NULL;
- //lwsl_notice(" adding pmo / cgi-env '%s' = '%s'\n", mp_cgienv->name,
- // mp_cgienv->value);
- goto dostring;
-
- case LEJPVP_PROTOCOL_NAME_OPT:
- /* this catches, eg,
- * vhosts[].ws-protocols[].xxx-protocol.yyy-option
- * ie, these are options attached to a protocol with { }
- */
- pvo = lwsws_align(a);
- a->p += sizeof(*a->pvo);
-
- n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
- /* ie, enable this protocol, no options yet */
- pvo->next = a->pvo->options;
- a->pvo->options = pvo;
- pvo->name = a->p;
- a->p += n;
- pvo->value = a->p;
- pvo->options = NULL;
- break;
-
- case LEJPVP_MOUNT_EXTRA_MIMETYPES:
- a->pvo_em = lwsws_align(a);
- a->p += sizeof(*a->pvo_em);
-
- n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
- /* ie, enable this protocol, no options yet */
- a->pvo_em->next = a->m.extra_mimetypes;
- a->m.extra_mimetypes = a->pvo_em;
- a->pvo_em->name = a->p;
- lwsl_notice(" adding extra-mimetypes %s -> %s\n", a->p, ctx->buf);
- a->p += n;
- a->pvo_em->value = a->p;
- a->pvo_em->options = NULL;
- break;
-
- case LEJPVP_MOUNT_INTERPRET:
- a->pvo_int = lwsws_align(a);
- a->p += sizeof(*a->pvo_int);
-
- n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
- /* ie, enable this protocol, no options yet */
- a->pvo_int->next = a->m.interpret;
- a->m.interpret = a->pvo_int;
- a->pvo_int->name = a->p;
- lwsl_notice(" adding interpret %s -> %s\n", a->p,
- ctx->buf);
- a->p += n;
- a->pvo_int->value = a->p;
- a->pvo_int->options = NULL;
- break;
-
- case LEJPVP_ENABLE_CLIENT_SSL:
- a->enable_client_ssl = arg_to_bool(ctx->buf);
- return 0;
- case LEJPVP_CLIENT_SSL_KEY:
- a->info->client_ssl_private_key_filepath = a->p;
- break;
- case LEJPVP_CLIENT_SSL_CERT:
- a->info->client_ssl_cert_filepath = a->p;
- break;
- case LEJPVP_CLIENT_SSL_CA:
- a->info->client_ssl_ca_filepath = a->p;
- break;
-
- case LEJPVP_NOIPV6:
- if (arg_to_bool(ctx->buf))
- a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6;
- else
- a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6);
- return 0;
-
- case LEJPVP_FLAG_ONLYRAW:
- if (arg_to_bool(ctx->buf))
- a->info->options |= LWS_SERVER_OPTION_ONLY_RAW;
- else
- a->info->options &= ~(LWS_SERVER_OPTION_ONLY_RAW);
- return 0;
-
- case LEJPVP_IPV6ONLY:
- a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY;
- if (arg_to_bool(ctx->buf))
- a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE;
- else
- a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
- return 0;
-
- case LEJPVP_SSL_OPTION_SET:
- a->info->ssl_options_set |= atol(ctx->buf);
- return 0;
- case LEJPVP_SSL_OPTION_CLEAR:
- a->info->ssl_options_clear |= atol(ctx->buf);
- return 0;
-
- default:
- return 0;
- }
-
-dostring:
- p = ctx->buf;
- p1 = strstr(p, ESC_INSTALL_DATADIR);
- if (p1) {
- n = p1 - p;
- if (n > a->end - a->p)
- n = a->end - a->p;
- strncpy(a->p, p, n);
- a->p += n;
- a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
- p += n + strlen(ESC_INSTALL_DATADIR);
- }
-
- a->p += lws_snprintf(a->p, a->end - a->p, "%s", p);
- *(a->p)++ = '\0';
-
- return 0;
-}
-
-/*
- * returns 0 = OK, 1 = can't open, 2 = parsing error
- */
-
-static int
-lwsws_get_config(void *user, const char *f, const char * const *paths,
- int count_paths, lejp_callback cb)
-{
- unsigned char buf[128];
- struct lejp_ctx ctx;
- int n, m, fd;
-
- fd = open(f, O_RDONLY);
- if (fd < 0) {
- lwsl_err("Cannot open %s\n", f);
- return 2;
- }
- lwsl_info("%s: %s\n", __func__, f);
- lejp_construct(&ctx, cb, user, paths, count_paths);
-
- do {
- n = read(fd, buf, sizeof(buf));
- if (!n)
- break;
-
- m = (int)(signed char)lejp_parse(&ctx, buf, n);
- } while (m == LEJP_CONTINUE);
-
- close(fd);
- n = ctx.line;
- lejp_destruct(&ctx);
-
- if (m < 0) {
- lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m,
- parser_errs[-m]);
- return 2;
- }
-
- return 0;
-}
-
-#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0
-
-static int
-lwsws_get_config_d(void *user, const char *d, const char * const *paths,
- int count_paths, lejp_callback cb)
-{
- uv_dirent_t dent;
- uv_fs_t req;
- char path[256];
- int ret = 0, ir;
- uv_loop_t loop;
-
- ir = uv_loop_init(&loop);
- if (ir) {
- lwsl_err("%s: loop init failed %d\n", __func__, ir);
- }
-
- if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) {
- lwsl_err("Scandir on %s failed\n", d);
- return 2;
- }
-
- while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
- lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
- ret = lwsws_get_config(user, path, paths, count_paths, cb);
- if (ret)
- goto bail;
- }
-
-bail:
- uv_fs_req_cleanup(&req);
- while (uv_loop_close(&loop))
- ;
-
- return ret;
-}
-
-#else
-
-#ifndef _WIN32
-static int filter(const struct dirent *ent)
-{
- if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
- return 0;
-
- return 1;
-}
-#endif
-
-static int
-lwsws_get_config_d(void *user, const char *d, const char * const *paths,
- int count_paths, lejp_callback cb)
-{
-#ifndef _WIN32
- struct dirent **namelist;
- char path[256];
- int n, i, ret = 0;
-
- 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 (strchr(namelist[i]->d_name, '~'))
- goto skip;
- lws_snprintf(path, sizeof(path) - 1, "%s/%s", d,
- namelist[i]->d_name);
- ret = lwsws_get_config(user, path, paths, count_paths, cb);
- if (ret) {
- while (i++ < n)
- free(namelist[i]);
- goto bail;
- }
-skip:
- free(namelist[i]);
- }
-
-bail:
- free(namelist);
-
- return ret;
-#else
- return 0;
-#endif
-}
-
-#endif
-
-int
-lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
- char **cs, int *len)
-{
- struct jpargs a;
- const char * const *old = info->plugin_dirs;
- char dd[128];
-
- memset(&a, 0, sizeof(a));
-
- a.info = info;
- a.p = *cs;
- a.end = (a.p + *len) - 1;
- a.valid = 0;
-
- lwsws_align(&a);
- info->plugin_dirs = (void *)a.p;
- a.plugin_dirs = (void *)a.p; /* writeable version */
- a.p += MAX_PLUGIN_DIRS * sizeof(void *);
-
- /* copy any default paths */
-
- while (old && *old) {
- a.plugin_dirs[a.count_plugin_dirs++] = *old;
- old++;
- }
-
- lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
- if (lwsws_get_config(&a, dd, paths_global,
- ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
- return 1;
- lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
- if (lwsws_get_config_d(&a, dd, paths_global,
- ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
- return 1;
-
- a.plugin_dirs[a.count_plugin_dirs] = NULL;
-
- *cs = a.p;
- *len = a.end - a.p;
-
- return 0;
-}
-
-int
-lwsws_get_config_vhosts(struct lws_context *context,
- struct lws_context_creation_info *info, const char *d,
- char **cs, int *len)
-{
- struct jpargs a;
- char dd[128];
-
- memset(&a, 0, sizeof(a));
-
- a.info = info;
- a.p = *cs;
- a.end = a.p + *len;
- a.valid = 0;
- a.context = context;
- a.protocols = info->protocols;
- a.extensions = info->extensions;
-
- lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
- if (lwsws_get_config(&a, dd, paths_vhosts,
- ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
- return 1;
- lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
- if (lwsws_get_config_d(&a, dd, paths_vhosts,
- ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
- return 1;
-
- *cs = a.p;
- *len = a.end - a.p;
-
- if (!a.any_vhosts) {
- lwsl_err("Need at least one vhost\n");
- return 1;
- }
-
-// lws_finalize_startup(context);
-
- return 0;
-}
diff --git a/thirdparty/lws/server/parsers.c b/thirdparty/lws/server/parsers.c
deleted file mode 100644
index fb345ab04c..0000000000
--- a/thirdparty/lws/server/parsers.c
+++ /dev/null
@@ -1,1783 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-const unsigned char lextable[] = {
- #include "lextable.h"
-};
-
-#define FAIL_CHAR 0x08
-
-int LWS_WARN_UNUSED_RESULT
-lextable_decode(int pos, char c)
-{
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A';
-
- while (1) {
- if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
- if ((lextable[pos] & 0x7f) != c)
- return -1;
- /* fall thru */
- pos++;
- if (lextable[pos] == FAIL_CHAR)
- return -1;
- return pos;
- }
-
- if (lextable[pos] == FAIL_CHAR)
- return -1;
-
- /* b7 = 0, end or 3-byte */
- if (lextable[pos] < FAIL_CHAR) /* terminal marker */
- return pos;
-
- if (lextable[pos] == c) /* goto */
- return pos + (lextable[pos + 1]) +
- (lextable[pos + 2] << 8);
- /* fall thru goto */
- pos += 3;
- /* continue */
- }
-}
-
-static struct allocated_headers *
-_lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
-{
- struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct");
-
- if (!ah)
- return NULL;
-
- ah->data = lws_malloc(data_size, "ah data");
- if (!ah->data) {
- lws_free(ah);
-
- return NULL;
- }
- ah->next = pt->ah_list;
- pt->ah_list = ah;
- ah->data_length = data_size;
- pt->ah_pool_length++;
-
- lwsl_info("%s: created ah %p (size %d): pool length %d\n", __func__,
- ah, (int)data_size, pt->ah_pool_length);
-
- return ah;
-}
-
-int
-_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
-{
- lws_start_foreach_llp(struct allocated_headers **, a, pt->ah_list) {
- if ((*a) == ah) {
- *a = ah->next;
- pt->ah_pool_length--;
- lwsl_info("%s: freed ah %p : pool length %d\n",
- __func__, ah, pt->ah_pool_length);
- if (ah->data)
- lws_free(ah->data);
- lws_free(ah);
-
- return 0;
- }
- } lws_end_foreach_llp(a, next);
-
- return 1;
-}
-
-void
-_lws_header_table_reset(struct allocated_headers *ah)
-{
- /* init the ah to reflect no headers or data have appeared yet */
- memset(ah->frag_index, 0, sizeof(ah->frag_index));
- memset(ah->frags, 0, sizeof(ah->frags));
- ah->nfrag = 0;
- ah->pos = 0;
- ah->http_response = 0;
-}
-
-// doesn't scrub the ah rxbuffer by default, parent must do if needed
-
-void
-lws_header_table_reset(struct lws *wsi, int autoservice)
-{
- struct allocated_headers *ah = wsi->u.hdr.ah;
- struct lws_context_per_thread *pt;
- struct lws_pollfd *pfd;
-
- /* if we have the idea we're resetting 'our' ah, must be bound to one */
- assert(ah);
- /* ah also concurs with ownership */
- assert(ah->wsi == wsi);
-
- _lws_header_table_reset(ah);
-
- wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
- wsi->u.hdr.lextable_pos = 0;
-
- /* since we will restart the ah, our new headers are not completed */
- wsi->hdr_parsing_completed = 0;
-
- /* while we hold the ah, keep a timeout on the wsi */
- lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
- wsi->vhost->timeout_secs_ah_idle);
-
- time(&ah->assigned);
-
- /*
- * if we inherited pending rx (from socket adoption deferred
- * processing), apply and free it.
- */
- if (wsi->u.hdr.preamble_rx) {
- memcpy(ah->rx, wsi->u.hdr.preamble_rx,
- wsi->u.hdr.preamble_rx_len);
- ah->rxlen = wsi->u.hdr.preamble_rx_len;
- lws_free_set_NULL(wsi->u.hdr.preamble_rx);
-
- if (autoservice) {
- lwsl_debug("%s: service on readbuf ah\n", __func__);
-
- pt = &wsi->context->pt[(int)wsi->tsi];
- /*
- * Unlike a normal connect, we have the headers already
- * (or the first part of them anyway)
- */
- pfd = &pt->fds[wsi->position_in_fds_table];
- pfd->revents |= LWS_POLLIN;
- lwsl_err("%s: calling service\n", __func__);
- lws_service_fd_tsi(wsi->context, pfd, wsi->tsi);
- }
- }
-}
-
-static void
-_lws_header_ensure_we_are_on_waiting_list(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- struct lws_pollargs pa;
- struct lws **pwsi = &pt->ah_wait_list;
-
- while (*pwsi) {
- if (*pwsi == wsi)
- return;
- pwsi = &(*pwsi)->u.hdr.ah_wait_list;
- }
-
- lwsl_info("%s: wsi: %p\n", __func__, wsi);
- wsi->u.hdr.ah_wait_list = pt->ah_wait_list;
- pt->ah_wait_list = wsi;
- pt->ah_wait_list_length++;
-
- /* we cannot accept input then */
-
- _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa);
-}
-
-static int
-__lws_remove_from_ah_waiting_list(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- struct lws **pwsi =&pt->ah_wait_list;
-
- while (*pwsi) {
- if (*pwsi == wsi) {
- lwsl_info("%s: wsi %p\n", __func__, wsi);
- /* point prev guy to our next */
- *pwsi = wsi->u.hdr.ah_wait_list;
- /* we shouldn't point anywhere now */
- wsi->u.hdr.ah_wait_list = NULL;
- pt->ah_wait_list_length--;
-
- return 1;
- }
- pwsi = &(*pwsi)->u.hdr.ah_wait_list;
- }
-
- return 0;
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_header_table_attach(struct lws *wsi, int autoservice)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct lws_pollargs pa;
- int n;
-
- lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__,
- (void *)wsi, (void *)wsi->u.hdr.ah, wsi->tsi,
- pt->ah_count_in_use);
-
- /* if we are already bound to one, just clear it down */
- if (wsi->u.hdr.ah) {
- lwsl_info("%s: cleardown\n", __func__);
- goto reset;
- }
-
- lws_pt_lock(pt);
-
- n = pt->ah_count_in_use == context->max_http_header_pool;
-#if defined(LWS_WITH_PEER_LIMITS)
- if (!n) {
- n = lws_peer_confirm_ah_attach_ok(context, wsi->peer);
- if (n)
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
- }
-#endif
- if (n) {
- /*
- * Pool is either all busy, or we don't want to give this
- * particular guy an ah right now...
- *
- * Make sure we are on the waiting list, and return that we
- * weren't able to provide the ah
- */
- _lws_header_ensure_we_are_on_waiting_list(wsi);
-
- goto bail;
- }
-
- __lws_remove_from_ah_waiting_list(wsi);
-
- wsi->u.hdr.ah = _lws_create_ah(pt, context->max_http_header_data);
- if (!wsi->u.hdr.ah) { /* we could not create an ah */
- _lws_header_ensure_we_are_on_waiting_list(wsi);
-
- goto bail;
- }
-
- wsi->u.hdr.ah->in_use = 1;
- wsi->u.hdr.ah->wsi = wsi; /* mark our owner */
- pt->ah_count_in_use++;
-
-#if defined(LWS_WITH_PEER_LIMITS)
- if (wsi->peer)
- wsi->peer->count_ah++;
-#endif
-
- _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
-
- lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__,
- (void *)wsi, (void *)wsi->u.hdr.ah, pt->ah_count_in_use);
-
- lws_pt_unlock(pt);
-
-reset:
-
- /* and reset the rx state */
- wsi->u.hdr.ah->rxpos = 0;
- wsi->u.hdr.ah->rxlen = 0;
-
- lws_header_table_reset(wsi, autoservice);
-
-#ifndef LWS_NO_CLIENT
- if (wsi->state == LWSS_CLIENT_UNCONNECTED)
- if (!lws_client_connect_via_info2(wsi))
- /* our client connect has failed, the wsi
- * has been closed
- */
- return -1;
-#endif
-
- return 0;
-
-bail:
- lws_pt_unlock(pt);
-
- return 1;
-}
-
-void
-lws_header_table_force_to_detachable_state(struct lws *wsi)
-{
- if (wsi->u.hdr.ah) {
- wsi->u.hdr.ah->rxpos = -1;
- wsi->u.hdr.ah->rxlen = -1;
- wsi->hdr_parsing_completed = 1;
- }
-}
-
-int
-lws_header_table_is_in_detachable_state(struct lws *wsi)
-{
- struct allocated_headers *ah = wsi->u.hdr.ah;
-
- return ah && ah->rxpos == ah->rxlen && wsi->hdr_parsing_completed;
-}
-
-int lws_header_table_detach(struct lws *wsi, int autoservice)
-{
- struct lws_context *context = wsi->context;
- struct allocated_headers *ah = wsi->u.hdr.ah;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct lws_pollargs pa;
- struct lws **pwsi, **pwsi_eligible;
- time_t now;
-
- lws_pt_lock(pt);
- __lws_remove_from_ah_waiting_list(wsi);
- lws_pt_unlock(pt);
-
- if (!ah)
- return 0;
-
- lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
- (void *)wsi, (void *)ah, wsi->tsi,
- pt->ah_count_in_use);
-
- if (wsi->u.hdr.preamble_rx)
- lws_free_set_NULL(wsi->u.hdr.preamble_rx);
-
- /* may not be detached while he still has unprocessed rx */
- if (!lws_header_table_is_in_detachable_state(wsi)) {
- lwsl_err("%s: %p: CANNOT DETACH rxpos:%d, rxlen:%d, "
- "wsi->hdr_parsing_completed = %d\n", __func__, wsi,
- ah->rxpos, ah->rxlen, wsi->hdr_parsing_completed);
- return 0;
- }
-
- lws_pt_lock(pt);
-
- /* we did have an ah attached */
- time(&now);
- if (ah->assigned && now - ah->assigned > 3) {
- /*
- * we're detaching the ah, but it was held an
- * unreasonably long time
- */
- lwsl_debug("%s: wsi %p: ah held %ds, "
- "ah.rxpos %d, ah.rxlen %d, mode/state %d %d,"
- "wsi->more_rx_waiting %d\n", __func__, wsi,
- (int)(now - ah->assigned),
- ah->rxpos, ah->rxlen, wsi->mode, wsi->state,
- wsi->more_rx_waiting);
- }
-
- ah->assigned = 0;
-
- /* if we think we're detaching one, there should be one in use */
- assert(pt->ah_count_in_use > 0);
- /* and this specific one should have been in use */
- assert(ah->in_use);
- wsi->u.hdr.ah = NULL;
- ah->wsi = NULL; /* no owner */
-#if defined(LWS_WITH_PEER_LIMITS)
- lws_peer_track_ah_detach(context, wsi->peer);
-#endif
-
- pwsi = &pt->ah_wait_list;
-
- /* oh there is nobody on the waiting list... leave the ah unattached */
- if (!*pwsi)
- goto nobody_usable_waiting;
-
- /*
- * at least one wsi on the same tsi is waiting, give it to oldest guy
- * who is allowed to take it (if any)
- */
- lwsl_info("pt wait list %p\n", *pwsi);
- wsi = NULL;
- pwsi_eligible = NULL;
-
- while (*pwsi) {
-#if defined(LWS_WITH_PEER_LIMITS)
- /* are we willing to give this guy an ah? */
- if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer))
-#endif
- {
- wsi = *pwsi;
- pwsi_eligible = pwsi;
- }
-#if defined(LWS_WITH_PEER_LIMITS)
- else
- if (!(*pwsi)->u.hdr.ah_wait_list)
- lws_stats_atomic_bump(context, pt,
- LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
-#endif
- pwsi = &(*pwsi)->u.hdr.ah_wait_list;
- }
-
- if (!wsi) /* everybody waiting already has too many ah... */
- goto nobody_usable_waiting;
-
- lwsl_info("%s: last eligible wsi in wait list %p\n", __func__, wsi);
-
- wsi->u.hdr.ah = ah;
- ah->wsi = wsi; /* new owner */
-
- /* and reset the rx state */
- ah->rxpos = 0;
- ah->rxlen = 0;
- lws_header_table_reset(wsi, autoservice);
-#if defined(LWS_WITH_PEER_LIMITS)
- if (wsi->peer)
- wsi->peer->count_ah++;
-#endif
-
- /* clients acquire the ah and then insert themselves in fds table... */
- if (wsi->position_in_fds_table != -1) {
- lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi);
-
- /* he has been stuck waiting for an ah, but now his wait is
- * over, let him progress */
-
- _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
- }
-
- /* point prev guy to next guy in list instead */
- *pwsi_eligible = wsi->u.hdr.ah_wait_list;
- /* the guy who got one is out of the list */
- wsi->u.hdr.ah_wait_list = NULL;
- pt->ah_wait_list_length--;
-
-#ifndef LWS_NO_CLIENT
- if (wsi->state == LWSS_CLIENT_UNCONNECTED) {
- lws_pt_unlock(pt);
-
- if (!lws_client_connect_via_info2(wsi)) {
- /* our client connect has failed, the wsi
- * has been closed
- */
-
- return -1;
- }
- return 0;
- }
-#endif
-
- assert(!!pt->ah_wait_list_length == !!(lws_intptr_t)pt->ah_wait_list);
-bail:
- lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
- (void *)wsi, (void *)ah, pt->tid, pt->ah_count_in_use);
-
- lws_pt_unlock(pt);
-
- return 0;
-
-nobody_usable_waiting:
- lwsl_info("%s: nobody usable waiting\n", __func__);
- _lws_destroy_ah(pt, ah);
- pt->ah_count_in_use--;
-
- goto bail;
-}
-
-LWS_VISIBLE int
-lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
-{
- int n;
-
- if (!wsi->u.hdr.ah)
- return 0;
-
- n = wsi->u.hdr.ah->frag_index[h];
- if (!n)
- return 0;
- do {
- if (!frag_idx)
- return wsi->u.hdr.ah->frags[n].len;
- n = wsi->u.hdr.ah->frags[n].nfrag;
- } while (frag_idx-- && n);
-
- return 0;
-}
-
-LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
-{
- int n;
- int len = 0;
-
- if (!wsi->u.hdr.ah)
- return 0;
-
- n = wsi->u.hdr.ah->frag_index[h];
- if (!n)
- return 0;
- do {
- len += wsi->u.hdr.ah->frags[n].len;
- n = wsi->u.hdr.ah->frags[n].nfrag;
- } while (n);
-
- return len;
-}
-
-LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
- enum lws_token_indexes h, int frag_idx)
-{
- int n = 0;
- int f;
-
- if (!wsi->u.hdr.ah)
- return -1;
-
- f = wsi->u.hdr.ah->frag_index[h];
-
- if (!f)
- return -1;
-
- while (n < frag_idx) {
- f = wsi->u.hdr.ah->frags[f].nfrag;
- if (!f)
- return -1;
- n++;
- }
-
- if (wsi->u.hdr.ah->frags[f].len >= len)
- return -1;
-
- memcpy(dst, wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[f].offset,
- wsi->u.hdr.ah->frags[f].len);
- dst[wsi->u.hdr.ah->frags[f].len] = '\0';
-
- return wsi->u.hdr.ah->frags[f].len;
-}
-
-LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len,
- enum lws_token_indexes h)
-{
- int toklen = lws_hdr_total_length(wsi, h);
- int n;
-
- if (toklen >= len)
- return -1;
-
- if (!wsi->u.hdr.ah)
- return -1;
-
- n = wsi->u.hdr.ah->frag_index[h];
- if (!n)
- return 0;
-
- do {
- if (wsi->u.hdr.ah->frags[n].len >= len)
- return -1;
- strncpy(dst, &wsi->u.hdr.ah->data[wsi->u.hdr.ah->frags[n].offset],
- wsi->u.hdr.ah->frags[n].len);
- dst += wsi->u.hdr.ah->frags[n].len;
- len -= wsi->u.hdr.ah->frags[n].len;
- n = wsi->u.hdr.ah->frags[n].nfrag;
- } while (n);
- *dst = '\0';
-
- return toklen;
-}
-
-char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
-{
- int n;
-
- n = wsi->u.hdr.ah->frag_index[h];
- if (!n)
- return NULL;
-
- return wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[n].offset;
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_pos_in_bounds(struct lws *wsi)
-{
- if (wsi->u.hdr.ah->pos <
- (unsigned int)wsi->context->max_http_header_data)
- return 0;
-
- if (wsi->u.hdr.ah->pos == wsi->context->max_http_header_data) {
- lwsl_err("Ran out of header data space\n");
- return 1;
- }
-
- /*
- * with these tests everywhere, it should never be able to exceed
- * the limit, only meet it
- */
- lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->u.hdr.ah->pos,
- wsi->context->max_http_header_data);
- assert(0);
-
- return 1;
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
-{
- wsi->u.hdr.ah->nfrag++;
- if (wsi->u.hdr.ah->nfrag == ARRAY_SIZE(wsi->u.hdr.ah->frags)) {
- lwsl_warn("More hdr frags than we can deal with, dropping\n");
- return -1;
- }
-
- wsi->u.hdr.ah->frag_index[h] = wsi->u.hdr.ah->nfrag;
-
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].offset = wsi->u.hdr.ah->pos;
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len = 0;
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].nfrag = 0;
-
- do {
- if (lws_pos_in_bounds(wsi))
- return -1;
-
- wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = *s;
- if (*s)
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++;
- } while (*s++);
-
- return 0;
-}
-
-signed char char_to_hex(const char c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
-
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
-
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
-
- return -1;
-}
-
-static int LWS_WARN_UNUSED_RESULT
-issue_char(struct lws *wsi, unsigned char c)
-{
- unsigned short frag_len;
-
- if (lws_pos_in_bounds(wsi))
- return -1;
-
- frag_len = wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len;
- /*
- * If we haven't hit the token limit, just copy the character into
- * the header
- */
- if (frag_len < wsi->u.hdr.current_token_limit) {
- wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c;
- if (c)
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++;
- return 0;
- }
-
- /* Insert a null character when we *hit* the limit: */
- if (frag_len == wsi->u.hdr.current_token_limit) {
- if (lws_pos_in_bounds(wsi))
- return -1;
-
- wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0';
- lwsl_warn("header %i exceeds limit %d\n",
- wsi->u.hdr.parser_state,
- wsi->u.hdr.current_token_limit);
- }
-
- return 1;
-}
-
-int
-lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
-{
- struct allocated_headers *ah = wsi->u.hdr.ah;
- unsigned int enc = 0;
- uint8_t c = *_c;
-
- /*
- * PRIORITY 1
- * special URI processing... convert %xx
- */
- switch (wsi->u.hdr.ues) {
- case URIES_IDLE:
- if (c == '%') {
- wsi->u.hdr.ues = URIES_SEEN_PERCENT;
- goto swallow;
- }
- break;
- case URIES_SEEN_PERCENT:
- if (char_to_hex(c) < 0)
- /* illegal post-% char */
- goto forbid;
-
- wsi->u.hdr.esc_stash = c;
- wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
- goto swallow;
-
- case URIES_SEEN_PERCENT_H1:
- if (char_to_hex(c) < 0)
- /* illegal post-% char */
- goto forbid;
-
- *_c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
- char_to_hex(c);
- c = *_c;
- enc = 1;
- wsi->u.hdr.ues = URIES_IDLE;
- break;
- }
-
- /*
- * PRIORITY 2
- * special URI processing...
- * convert /.. or /... or /../ etc to /
- * convert /./ to /
- * convert // or /// etc to /
- * leave /.dir or whatever alone
- */
-
- switch (wsi->u.hdr.ups) {
- case URIPS_IDLE:
- if (!c)
- return -1;
- /* genuine delimiter */
- if ((c == '&' || c == ';') && !enc) {
- if (issue_char(wsi, c) < 0)
- return -1;
- /* swallow the terminator */
- ah->frags[ah->nfrag].len--;
- /* link to next fragment */
- ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
- ah->nfrag++;
- if (ah->nfrag >= ARRAY_SIZE(ah->frags))
- goto excessive;
- /* start next fragment after the & */
- wsi->u.hdr.post_literal_equal = 0;
- ah->frags[ah->nfrag].offset = ah->pos;
- ah->frags[ah->nfrag].len = 0;
- ah->frags[ah->nfrag].nfrag = 0;
- goto swallow;
- }
- /* uriencoded = in the name part, disallow */
- if (c == '=' && enc &&
- ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
- !wsi->u.hdr.post_literal_equal) {
- c = '_';
- *_c =c;
- }
-
- /* after the real =, we don't care how many = */
- if (c == '=' && !enc)
- wsi->u.hdr.post_literal_equal = 1;
-
- /* + to space */
- if (c == '+' && !enc) {
- c = ' ';
- *_c = c;
- }
- /* issue the first / always */
- if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
- wsi->u.hdr.ups = URIPS_SEEN_SLASH;
- break;
- case URIPS_SEEN_SLASH:
- /* swallow subsequent slashes */
- if (c == '/')
- goto swallow;
- /* track and swallow the first . after / */
- if (c == '.') {
- wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT;
- goto swallow;
- }
- wsi->u.hdr.ups = URIPS_IDLE;
- break;
- case URIPS_SEEN_SLASH_DOT:
- /* swallow second . */
- if (c == '.') {
- wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
- goto swallow;
- }
- /* change /./ to / */
- if (c == '/') {
- wsi->u.hdr.ups = URIPS_SEEN_SLASH;
- goto swallow;
- }
- /* it was like /.dir ... regurgitate the . */
- wsi->u.hdr.ups = URIPS_IDLE;
- if (issue_char(wsi, '.') < 0)
- return -1;
- break;
-
- case URIPS_SEEN_SLASH_DOT_DOT:
-
- /* /../ or /..[End of URI] --> backup to last / */
- if (c == '/' || c == '?') {
- /*
- * back up one dir level if possible
- * safe against header fragmentation because
- * the method URI can only be in 1 fragment
- */
- if (ah->frags[ah->nfrag].len > 2) {
- ah->pos--;
- ah->frags[ah->nfrag].len--;
- do {
- ah->pos--;
- ah->frags[ah->nfrag].len--;
- } while (ah->frags[ah->nfrag].len > 1 &&
- ah->data[ah->pos] != '/');
- }
- wsi->u.hdr.ups = URIPS_SEEN_SLASH;
- if (ah->frags[ah->nfrag].len > 1)
- break;
- goto swallow;
- }
-
- /* /..[^/] ... regurgitate and allow */
-
- if (issue_char(wsi, '.') < 0)
- return -1;
- if (issue_char(wsi, '.') < 0)
- return -1;
- wsi->u.hdr.ups = URIPS_IDLE;
- break;
- }
-
- if (c == '?' && !enc &&
- !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */
- if (wsi->u.hdr.ues != URIES_IDLE)
- goto forbid;
-
- /* seal off uri header */
- if (issue_char(wsi, '\0') < 0)
- return -1;
-
- /* move to using WSI_TOKEN_HTTP_URI_ARGS */
- ah->nfrag++;
- if (ah->nfrag >= ARRAY_SIZE(ah->frags))
- goto excessive;
- ah->frags[ah->nfrag].offset = ah->pos;
- ah->frags[ah->nfrag].len = 0;
- ah->frags[ah->nfrag].nfrag = 0;
-
- wsi->u.hdr.post_literal_equal = 0;
- ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
- wsi->u.hdr.ups = URIPS_IDLE;
- goto swallow;
- }
-
- return LPUR_CONTINUE;
-
-swallow:
- return LPUR_SWALLOW;
-
-forbid:
- return LPUR_FORBID;
-
-excessive:
- return LPUR_EXCESSIVE;
-}
-
-static const unsigned char methods[] = {
- WSI_TOKEN_GET_URI,
- WSI_TOKEN_POST_URI,
- WSI_TOKEN_OPTIONS_URI,
- WSI_TOKEN_PUT_URI,
- WSI_TOKEN_PATCH_URI,
- WSI_TOKEN_DELETE_URI,
- WSI_TOKEN_CONNECT,
- WSI_TOKEN_HEAD_URI,
-};
-
-int LWS_WARN_UNUSED_RESULT
-lws_parse(struct lws *wsi, unsigned char c)
-{
- struct allocated_headers *ah = wsi->u.hdr.ah;
- struct lws_context *context = wsi->context;
- unsigned int n, m;
- int r;
-
- assert(wsi->u.hdr.ah);
-
- switch (wsi->u.hdr.parser_state) {
- default:
-
- lwsl_parser("WSI_TOK_(%d) '%c'\n", wsi->u.hdr.parser_state, c);
-
- /* collect into malloc'd buffers */
- /* optional initial space swallow */
- if (!ah->frags[ah->frag_index[wsi->u.hdr.parser_state]].len &&
- c == ' ')
- break;
-
- for (m = 0; m < ARRAY_SIZE(methods); m++)
- if (wsi->u.hdr.parser_state == methods[m])
- break;
- if (m == ARRAY_SIZE(methods))
- /* it was not any of the methods */
- goto check_eol;
-
- /* special URI processing... end at space */
-
- if (c == ' ') {
- /* enforce starting with / */
- if (!ah->frags[ah->nfrag].len)
- if (issue_char(wsi, '/') < 0)
- return -1;
-
- if (wsi->u.hdr.ups == URIPS_SEEN_SLASH_DOT_DOT) {
- /*
- * back up one dir level if possible
- * safe against header fragmentation because
- * the method URI can only be in 1 fragment
- */
- if (ah->frags[ah->nfrag].len > 2) {
- ah->pos--;
- ah->frags[ah->nfrag].len--;
- do {
- ah->pos--;
- ah->frags[ah->nfrag].len--;
- } while (ah->frags[ah->nfrag].len > 1 &&
- ah->data[ah->pos] != '/');
- }
- }
-
- /* begin parsing HTTP version: */
- if (issue_char(wsi, '\0') < 0)
- return -1;
- wsi->u.hdr.parser_state = WSI_TOKEN_HTTP;
- goto start_fragment;
- }
-
- r = lws_parse_urldecode(wsi, &c);
- switch (r) {
- case LPUR_CONTINUE:
- break;
- case LPUR_SWALLOW:
- goto swallow;
- case LPUR_FORBID:
- goto forbid;
- case LPUR_EXCESSIVE:
- goto excessive;
- default:
- return -1;
- }
-check_eol:
- /* bail at EOL */
- if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE &&
- c == '\x0d') {
- if (wsi->u.hdr.ues != URIES_IDLE)
- goto forbid;
-
- c = '\0';
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
- lwsl_parser("*\n");
- }
-
- n = issue_char(wsi, c);
- if ((int)n < 0)
- return -1;
- if (n > 0)
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
-
-swallow:
- /* per-protocol end of headers management */
-
- if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE)
- goto set_parsing_complete;
- break;
-
- /* collecting and checking a name part */
- case WSI_TOKEN_NAME_PART:
- lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (mode=%d) wsi->u.hdr.lextable_pos=%d\n", c, c, wsi->mode, wsi->u.hdr.lextable_pos);
-
- wsi->u.hdr.lextable_pos =
- lextable_decode(wsi->u.hdr.lextable_pos, c);
- /*
- * Server needs to look out for unknown methods...
- */
- if (wsi->u.hdr.lextable_pos < 0 &&
- (wsi->mode == LWSCM_HTTP_SERVING)) {
- /* this is not a header we know about */
- for (m = 0; m < ARRAY_SIZE(methods); m++)
- if (ah->frag_index[methods[m]]) {
- /*
- * already had the method, no idea what
- * this crap from the client is, ignore
- */
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
- break;
- }
- /*
- * hm it's an unknown http method from a client in fact,
- * it cannot be valid http
- */
- if (m == ARRAY_SIZE(methods)) {
- /*
- * are we set up to accept raw in these cases?
- */
- if (lws_check_opt(wsi->vhost->options,
- LWS_SERVER_OPTION_FALLBACK_TO_RAW))
- return 2; /* transition to raw */
-
- lwsl_info("Unknown method - dropping\n");
- goto forbid;
- }
- break;
- }
- /*
- * ...otherwise for a client, let him ignore unknown headers
- * coming from the server
- */
- if (wsi->u.hdr.lextable_pos < 0) {
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
- break;
- }
-
- if (lextable[wsi->u.hdr.lextable_pos] < FAIL_CHAR) {
- /* terminal state */
-
- n = ((unsigned int)lextable[wsi->u.hdr.lextable_pos] << 8) |
- lextable[wsi->u.hdr.lextable_pos + 1];
-
- lwsl_parser("known hdr %d\n", n);
- for (m = 0; m < ARRAY_SIZE(methods); m++)
- if (n == methods[m] &&
- ah->frag_index[methods[m]]) {
- lwsl_warn("Duplicated method\n");
- return -1;
- }
-
- /*
- * WSORIGIN is protocol equiv to ORIGIN,
- * JWebSocket likes to send it, map to ORIGIN
- */
- if (n == WSI_TOKEN_SWORIGIN)
- n = WSI_TOKEN_ORIGIN;
-
- wsi->u.hdr.parser_state = (enum lws_token_indexes)
- (WSI_TOKEN_GET_URI + n);
-
- if (context->token_limits)
- wsi->u.hdr.current_token_limit =
- context->token_limits->token_limit[
- wsi->u.hdr.parser_state];
- else
- wsi->u.hdr.current_token_limit =
- wsi->context->max_http_header_data;
-
- if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE)
- goto set_parsing_complete;
-
- goto start_fragment;
- }
- break;
-
-start_fragment:
- ah->nfrag++;
-excessive:
- if (ah->nfrag == ARRAY_SIZE(ah->frags)) {
- lwsl_warn("More hdr frags than we can deal with\n");
- return -1;
- }
-
- ah->frags[ah->nfrag].offset = ah->pos;
- ah->frags[ah->nfrag].len = 0;
- ah->frags[ah->nfrag].nfrag = 0;
- ah->frags[ah->nfrag].flags = 2;
-
- n = ah->frag_index[wsi->u.hdr.parser_state];
- if (!n) { /* first fragment */
- ah->frag_index[wsi->u.hdr.parser_state] = ah->nfrag;
- ah->hdr_token_idx = wsi->u.hdr.parser_state;
- break;
- }
- /* continuation */
- while (ah->frags[n].nfrag)
- n = ah->frags[n].nfrag;
- ah->frags[n].nfrag = ah->nfrag;
-
- if (issue_char(wsi, ' ') < 0)
- return -1;
- break;
-
- /* skipping arg part of a name we didn't recognize */
- case WSI_TOKEN_SKIPPING:
- lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
-
- if (c == '\x0d')
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
- break;
-
- case WSI_TOKEN_SKIPPING_SAW_CR:
- lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
- if (wsi->u.hdr.ues != URIES_IDLE)
- goto forbid;
- if (c == '\x0a') {
- wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
- wsi->u.hdr.lextable_pos = 0;
- } else
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
- break;
- /* we're done, ignore anything else */
-
- case WSI_PARSING_COMPLETE:
- lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
- break;
- }
-
- return 0;
-
-set_parsing_complete:
- if (wsi->u.hdr.ues != URIES_IDLE)
- goto forbid;
- if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
- if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
- wsi->ietf_spec_revision =
- atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
-
- lwsl_parser("v%02d hdrs completed\n", wsi->ietf_spec_revision);
- }
- wsi->u.hdr.parser_state = WSI_PARSING_COMPLETE;
- wsi->hdr_parsing_completed = 1;
-
- return 0;
-
-forbid:
- lwsl_notice(" forbidding on uri sanitation\n");
- lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
-
- return -1;
-}
-
-LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
-{
- return wsi->u.ws.frame_is_binary;
-}
-
-void
-lws_add_wsi_to_draining_ext_list(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-
- if (wsi->u.ws.rx_draining_ext)
- return;
-
- lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__);
-
- 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;
-}
-
-void
-lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- struct lws **w = &pt->rx_draining_ext_list;
-
- if (!wsi->u.ws.rx_draining_ext)
- return;
-
- lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__);
-
- wsi->u.ws.rx_draining_ext = 0;
-
- /* remove us from context draining ext list */
- while (*w) {
- if (*w == wsi) {
- /* if us, point it instead to who we were pointing to */
- *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;
-}
-
-/*
- * client-parser.c: lws_client_rx_sm() needs to be roughly kept in
- * sync with changes here, esp related to ext draining
- */
-
-int
-lws_rx_sm(struct lws *wsi, unsigned char c)
-{
- int callback_action = LWS_CALLBACK_RECEIVE;
- int ret = 0, n, rx_draining_ext = 0;
- struct lws_tokens eff_buf;
-
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
- if (wsi->socket_is_permanently_unusable)
- return -1;
-
- switch (wsi->lws_rx_parse_state) {
- case LWS_RXPS_NEW:
- if (wsi->u.ws.rx_draining_ext) {
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
- lws_remove_wsi_from_draining_ext_list(wsi);
- rx_draining_ext = 1;
- lwsl_debug("%s: doing draining flow\n", __func__);
-
- goto drain_extension;
- }
- switch (wsi->ietf_spec_revision) {
- case 13:
- /*
- * no prepended frame key any more
- */
- wsi->u.ws.all_zero_nonce = 1;
- goto handle_first;
-
- default:
- lwsl_warn("lws_rx_sm: unknown spec version %d\n",
- wsi->ietf_spec_revision);
- break;
- }
- break;
- case LWS_RXPS_04_mask_1:
- wsi->u.ws.mask[1] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2;
- break;
- case LWS_RXPS_04_mask_2:
- wsi->u.ws.mask[2] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3;
- break;
- case LWS_RXPS_04_mask_3:
- wsi->u.ws.mask[3] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
-
- /*
- * start from the zero'th byte in the XOR key buffer since
- * this is the start of a frame with a new key
- */
-
- wsi->u.ws.mask_idx = 0;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
- break;
-
- /*
- * 04 logical framing from the spec (all this is masked when incoming
- * and has to be unmasked)
- *
- * We ignore the possibility of extension data because we don't
- * negotiate any extensions at the moment.
- *
- * 0 1 2 3
- * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- * +-+-+-+-+-------+-+-------------+-------------------------------+
- * |F|R|R|R| opcode|R| Payload len | Extended payload length |
- * |I|S|S|S| (4) |S| (7) | (16/63) |
- * |N|V|V|V| |V| | (if payload len==126/127) |
- * | |1|2|3| |4| | |
- * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
- * | Extended payload length continued, if payload len == 127 |
- * + - - - - - - - - - - - - - - - +-------------------------------+
- * | | Extension data |
- * +-------------------------------+ - - - - - - - - - - - - - - - +
- * : :
- * +---------------------------------------------------------------+
- * : Application data :
- * +---------------------------------------------------------------+
- *
- * We pass payload through to userland as soon as we get it, ignoring
- * FIN. It's up to userland to buffer it up if it wants to see a
- * whole unfragmented block of the original size (which may be up to
- * 2^63 long!)
- */
-
- case LWS_RXPS_04_FRAME_HDR_1:
-handle_first:
-
- wsi->u.ws.opcode = c & 0xf;
- wsi->u.ws.rsv = c & 0x70;
- wsi->u.ws.final = !!((c >> 7) & 1);
-
- switch (wsi->u.ws.opcode) {
- case LWSWSOPC_TEXT_FRAME:
- case LWSWSOPC_BINARY_FRAME:
- wsi->u.ws.rsv_first_msg = (c & 0x70);
- wsi->u.ws.frame_is_binary =
- wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME;
- wsi->u.ws.first_fragment = 1;
- break;
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 0xb:
- case 0xc:
- case 0xd:
- case 0xe:
- case 0xf:
- lwsl_info("illegal opcode\n");
- return -1;
- }
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN:
-
- wsi->u.ws.this_frame_masked = !!(c & 0x80);
-
- switch (c & 0x7f) {
- case 126:
- /* control frames are not allowed to have big lengths */
- if (wsi->u.ws.opcode & 8)
- goto illegal_ctl_length;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
- break;
- case 127:
- /* control frames are not allowed to have big lengths */
- if (wsi->u.ws.opcode & 8)
- goto illegal_ctl_length;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
- break;
- default:
- wsi->u.ws.rx_packet_length = c & 0x7f;
- if (wsi->u.ws.this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else
- if (wsi->u.ws.rx_packet_length)
- wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
- else {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- break;
- }
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN16_2:
- wsi->u.ws.rx_packet_length = c << 8;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN16_1:
- wsi->u.ws.rx_packet_length |= c;
- if (wsi->u.ws.this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else
- wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_8:
- if (c & 0x80) {
- lwsl_warn("b63 of length must be zero\n");
- /* kill the connection */
- return -1;
- }
-#if defined __LP64__
- wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
-#else
- wsi->u.ws.rx_packet_length = 0;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_7:
-#if defined __LP64__
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_6:
-#if defined __LP64__
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_5:
-#if defined __LP64__
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_4:
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_3:
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_2:
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_1:
- wsi->u.ws.rx_packet_length |= ((size_t)c);
- if (wsi->u.ws.this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else
- wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
- wsi->u.ws.mask[0] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
- wsi->u.ws.mask[1] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
- wsi->u.ws.mask[2] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
- wsi->u.ws.mask[3] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
- wsi->u.ws.mask_idx = 0;
- if (wsi->u.ws.rx_packet_length == 0) {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- break;
-
-
- case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
- assert(wsi->u.ws.rx_ubuf);
-
- if (wsi->u.ws.rx_draining_ext)
- goto drain_extension;
-
- if (wsi->u.ws.rx_ubuf_head + LWS_PRE >=
- wsi->u.ws.rx_ubuf_alloc) {
- lwsl_err("Attempted overflow \n");
- return -1;
- }
- if (wsi->u.ws.all_zero_nonce)
- wsi->u.ws.rx_ubuf[LWS_PRE +
- (wsi->u.ws.rx_ubuf_head++)] = c;
- else
- wsi->u.ws.rx_ubuf[LWS_PRE +
- (wsi->u.ws.rx_ubuf_head++)] =
- c ^ wsi->u.ws.mask[
- (wsi->u.ws.mask_idx++) & 3];
-
- if (--wsi->u.ws.rx_packet_length == 0) {
- /* spill because we have the whole frame */
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
-
- /*
- * if there's no protocol max frame size given, we are
- * supposed to default to context->pt_serv_buf_size
- */
- if (!wsi->protocol->rx_buffer_size &&
- wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size)
- break;
-
- if (wsi->protocol->rx_buffer_size &&
- wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size)
- break;
-
- /* spill because we filled our rx buffer */
-spill:
- /*
- * is this frame a control packet we should take care of at this
- * layer? If so service it and hide it from the user callback
- */
-
- lwsl_parser("spill on %s\n", wsi->protocol->name);
-
- switch (wsi->u.ws.opcode) {
- case LWSWSOPC_CLOSE:
-
- /* is this an acknowledgement of our close? */
- if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
- /*
- * fine he has told us he is closing too, let's
- * finish our close
- */
- lwsl_parser("seen client close ack\n");
- return -1;
- }
- if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
- /* if he sends us 2 CLOSE, kill him */
- return -1;
-
- if (lws_partial_buffered(wsi)) {
- /*
- * if we're in the middle of something,
- * we can't do a normal close response and
- * have to just close our end.
- */
- wsi->socket_is_permanently_unusable = 1;
- lwsl_parser("Closing on peer close due to Pending tx\n");
- return -1;
- }
-
- if (user_callback_handle_rxflow(
- wsi->protocol->callback, wsi,
- LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
- wsi->user_space,
- &wsi->u.ws.rx_ubuf[LWS_PRE],
- wsi->u.ws.rx_ubuf_head))
- return -1;
-
- lwsl_parser("server sees client close packet\n");
- wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
- /* deal with the close packet contents as a PONG */
- wsi->u.ws.payload_is_close = 1;
- goto process_as_ping;
-
- case LWSWSOPC_PING:
- lwsl_info("received %d byte ping, sending pong\n",
- wsi->u.ws.rx_ubuf_head);
-
- if (wsi->u.ws.ping_pending_flag) {
- /*
- * there is already a pending ping payload
- * we should just log and drop
- */
- lwsl_parser("DROP PING since one pending\n");
- goto ping_drop;
- }
-process_as_ping:
- /* control packets can only be < 128 bytes long */
- if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
- lwsl_parser("DROP PING payload too large\n");
- goto ping_drop;
- }
-
- /* stash the pong payload */
- memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE,
- &wsi->u.ws.rx_ubuf[LWS_PRE],
- wsi->u.ws.rx_ubuf_head);
-
- wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head;
- wsi->u.ws.ping_pending_flag = 1;
-
- /* get it sent as soon as possible */
- lws_callback_on_writable(wsi);
-ping_drop:
- wsi->u.ws.rx_ubuf_head = 0;
- return 0;
-
- case LWSWSOPC_PONG:
- lwsl_info("received pong\n");
- lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE],
- wsi->u.ws.rx_ubuf_head);
-
- if (wsi->pending_timeout == PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
- lwsl_info("received expected PONG on wsi %p\n", wsi);
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- }
-
- /* issue it */
- callback_action = LWS_CALLBACK_RECEIVE_PONG;
- break;
-
- case LWSWSOPC_TEXT_FRAME:
- case LWSWSOPC_BINARY_FRAME:
- case LWSWSOPC_CONTINUATION:
- break;
-
- default:
- lwsl_parser("passing opc %x up to exts\n",
- wsi->u.ws.opcode);
- /*
- * It's something special we can't understand here.
- * Pass the payload up to the extension's parsing
- * state machine.
- */
-
- eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
- eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
-
- if (lws_ext_cb_active(wsi, LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
- &eff_buf, 0) <= 0)
- /* not handle or fail */
- lwsl_ext("ext opc opcode 0x%x unknown\n",
- wsi->u.ws.opcode);
-
- wsi->u.ws.rx_ubuf_head = 0;
- return 0;
- }
-
- /*
- * No it's real payload, pass it up to the user callback.
- * It's nicely buffered with the pre-padding taken care of
- * so it can be sent straight out again using lws_write
- */
-
- eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
- eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
-
- if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len)
- goto already_done;
-
-drain_extension:
- lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
-
- if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
- wsi->state == LWSS_AWAITING_CLOSE_ACK)
- goto already_done;
-
- n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
- /*
- * eff_buf may be pointing somewhere completely different now,
- * it's the output
- */
- wsi->u.ws.first_fragment = 0;
- if (n < 0) {
- /*
- * we may rely on this to get RX, just drop connection
- */
- wsi->socket_is_permanently_unusable = 1;
- return -1;
- }
-
- if (rx_draining_ext && eff_buf.token_len == 0)
- goto already_done;
-
- if (n && eff_buf.token_len)
- /* extension had more... main loop will come back */
- lws_add_wsi_to_draining_ext_list(wsi);
- else
- lws_remove_wsi_from_draining_ext_list(wsi);
-
- if (eff_buf.token_len > 0 ||
- callback_action == LWS_CALLBACK_RECEIVE_PONG) {
- eff_buf.token[eff_buf.token_len] = '\0';
-
- if (wsi->protocol->callback) {
-
- if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
- lwsl_info("Doing pong callback\n");
-
- ret = user_callback_handle_rxflow(
- wsi->protocol->callback,
- wsi,
- (enum lws_callback_reasons)callback_action,
- wsi->user_space,
- eff_buf.token,
- eff_buf.token_len);
- }
- else
- lwsl_err("No callback on payload spill!\n");
- }
-
-already_done:
- wsi->u.ws.rx_ubuf_head = 0;
- break;
- }
-
- return ret;
-
-illegal_ctl_length:
-
- lwsl_warn("Control frame with xtended length is illegal\n");
- /* kill the connection */
- return -1;
-}
-
-LWS_VISIBLE size_t
-lws_remaining_packet_payload(struct lws *wsi)
-{
- return wsi->u.ws.rx_packet_length;
-}
-
-/* Once we reach LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED, we know how much
- * to expect in that state and can deal with it in bulk more efficiently.
- */
-
-int
-lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf,
- size_t *len)
-{
- unsigned char *buffer = *buf, mask[4];
- int buffer_size, n;
- unsigned int avail;
- char *rx_ubuf;
-
- if (wsi->protocol->rx_buffer_size)
- buffer_size = wsi->protocol->rx_buffer_size;
- else
- buffer_size = wsi->context->pt_serv_buf_size;
- avail = buffer_size - wsi->u.ws.rx_ubuf_head;
-
- /* do not consume more than we should */
- if (avail > wsi->u.ws.rx_packet_length)
- avail = wsi->u.ws.rx_packet_length;
-
- /* do not consume more than what is in the buffer */
- if (avail > *len)
- avail = *len;
-
- /* we want to leave 1 byte for the parser to handle properly */
- if (avail <= 1)
- return 0;
-
- avail--;
- rx_ubuf = wsi->u.ws.rx_ubuf + LWS_PRE + wsi->u.ws.rx_ubuf_head;
- if (wsi->u.ws.all_zero_nonce)
- memcpy(rx_ubuf, buffer, avail);
- else {
-
- for (n = 0; n < 4; n++)
- mask[n] = wsi->u.ws.mask[(wsi->u.ws.mask_idx + n) & 3];
-
- /* deal with 4-byte chunks using unwrapped loop */
- n = avail >> 2;
- while (n--) {
- *(rx_ubuf++) = *(buffer++) ^ mask[0];
- *(rx_ubuf++) = *(buffer++) ^ mask[1];
- *(rx_ubuf++) = *(buffer++) ^ mask[2];
- *(rx_ubuf++) = *(buffer++) ^ mask[3];
- }
- /* and the remaining bytes bytewise */
- for (n = 0; n < (int)(avail & 3); n++)
- *(rx_ubuf++) = *(buffer++) ^ mask[n];
-
- wsi->u.ws.mask_idx = (wsi->u.ws.mask_idx + avail) & 3;
- }
-
- (*buf) += avail;
- wsi->u.ws.rx_ubuf_head += avail;
- wsi->u.ws.rx_packet_length -= avail;
- *len -= avail;
-
- return avail;
-}
diff --git a/thirdparty/lws/server/ranges.c b/thirdparty/lws/server/ranges.c
deleted file mode 100644
index bc1578d733..0000000000
--- a/thirdparty/lws/server/ranges.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * RFC7233 ranges parser
- *
- * Copyright (C) 2016 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-/*
- * RFC7233 examples
- *
- * o The first 500 bytes (byte offsets 0-499, inclusive):
- *
- * bytes=0-499
- *
- * o The second 500 bytes (byte offsets 500-999, inclusive):
- *
- * bytes=500-999
- *
- * o The final 500 bytes (byte offsets 9500-9999, inclusive):
- *
- * bytes=-500
- *
- * Or:
- *
- * bytes=9500-
- *
- * o The first and last bytes only (bytes 0 and 9999):
- *
- * bytes=0-0,-1
- *
- * o Other valid (but not canonical) specifications of the second 500
- * bytes (byte offsets 500-999, inclusive):
- *
- * bytes=500-600,601-999
- * bytes=500-700,601-999
- */
-
-/*
- * returns 1 if the range struct represents a usable range
- * if no ranges header, you get one of these for the whole
- * file. Otherwise you get one for each valid range in the
- * header.
- *
- * returns 0 if no further valid range forthcoming; rp->state
- * may be LWSRS_SYNTAX or LWSRS_COMPLETED
- */
-
-int
-lws_ranges_next(struct lws_range_parsing *rp)
-{
- static const char * const beq = "bytes=";
- char c;
-
- while (1) {
-
- c = rp->buf[rp->pos];
-
- switch (rp->state) {
- case LWSRS_SYNTAX:
- case LWSRS_COMPLETED:
- return 0;
-
- case LWSRS_NO_ACTIVE_RANGE:
- rp->state = LWSRS_COMPLETED;
- return 0;
-
- case LWSRS_BYTES_EQ: // looking for "bytes="
- if (c != beq[rp->pos]) {
- rp->state = LWSRS_SYNTAX;
- return -1;
- }
- if (rp->pos == 5)
- rp->state = LWSRS_FIRST;
- break;
-
- case LWSRS_FIRST:
- rp->start = 0;
- rp->end = 0;
- rp->start_valid = 0;
- rp->end_valid = 0;
-
- rp->state = LWSRS_STARTING;
-
- // fallthru
-
- case LWSRS_STARTING:
- if (c == '-') {
- rp->state = LWSRS_ENDING;
- break;
- }
-
- if (!(c >= '0' && c <= '9')) {
- rp->state = LWSRS_SYNTAX;
- return 0;
- }
- rp->start = (rp->start * 10) + (c - '0');
- rp->start_valid = 1;
- break;
-
- case LWSRS_ENDING:
- if (c == ',' || c == '\0') {
- rp->state = LWSRS_FIRST;
- if (c == ',')
- rp->pos++;
-
- /*
- * By the end of this, start and end are
- * always valid if the range still is
- */
-
- if (!rp->start_valid) { /* eg, -500 */
- if (rp->end > rp->extent)
- rp->end = rp->extent;
-
- rp->start = rp->extent - rp->end;
- rp->end = rp->extent - 1;
- } else
- if (!rp->end_valid)
- rp->end = rp->extent - 1;
-
- rp->did_try = 1;
-
- /* end must be >= start or ignore it */
- if (rp->end < rp->start) {
- if (c == ',')
- break;
- rp->state = LWSRS_COMPLETED;
- return 0;
- }
-
- return 1; /* issue range */
- }
-
- if (!(c >= '0' && c <= '9')) {
- rp->state = LWSRS_SYNTAX;
- return 0;
- }
- rp->end = (rp->end * 10) + (c - '0');
- rp->end_valid = 1;
- break;
- }
-
- rp->pos++;
- }
-}
-
-void
-lws_ranges_reset(struct lws_range_parsing *rp)
-{
- rp->pos = 0;
- rp->ctr = 0;
- rp->start = 0;
- rp->end = 0;
- rp->start_valid = 0;
- rp->end_valid = 0;
- rp->state = LWSRS_BYTES_EQ;
-}
-
-/*
- * returns count of valid ranges
- */
-int
-lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
- unsigned long long extent)
-{
- rp->agg = 0;
- rp->send_ctr = 0;
- rp->inside = 0;
- rp->count_ranges = 0;
- rp->did_try = 0;
- lws_ranges_reset(rp);
- rp->state = LWSRS_COMPLETED;
-
- rp->extent = extent;
-
- if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf),
- WSI_TOKEN_HTTP_RANGE) <= 0)
- return 0;
-
- rp->state = LWSRS_BYTES_EQ;
-
- while (lws_ranges_next(rp)) {
- rp->count_ranges++;
- rp->agg += rp->end - rp->start + 1;
- }
-
- lwsl_debug("%s: count %d\n", __func__, rp->count_ranges);
- lws_ranges_reset(rp);
-
- if (rp->did_try && !rp->count_ranges)
- return -1; /* "not satisfiable */
-
- lws_ranges_next(rp);
-
- return rp->count_ranges;
-}
diff --git a/thirdparty/lws/server/server-handshake.c b/thirdparty/lws/server/server-handshake.c
deleted file mode 100644
index 3d319c35d6..0000000000
--- a/thirdparty/lws/server/server-handshake.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
-
-#ifndef LWS_NO_EXTENSIONS
-static int
-lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- char ext_name[64], *args, *end = (*p) + budget - 1;
- const struct lws_ext_options *opts, *po;
- const struct lws_extension *ext;
- struct lws_ext_option_arg oa;
- int n, m, more = 1;
- int ext_count = 0;
- char ignore;
- 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;
- ignore = 0;
- n = 0;
- args = NULL;
-
- /*
- * We may get a simple request
- *
- * Sec-WebSocket-Extensions: permessage-deflate
- *
- * or an elaborated one with requested options
- *
- * Sec-WebSocket-Extensions: permessage-deflate; \
- * server_no_context_takeover; \
- * client_no_context_takeover
- */
-
- while (more) {
-
- if (*c && (*c != ',' && *c != '\t')) {
- if (*c == ';') {
- ignore = 1;
- args = c + 1;
- }
- if (ignore || *c == ' ') {
- c++;
- continue;
- }
- ext_name[n] = *c++;
- if (n < sizeof(ext_name) - 1)
- n++;
- continue;
- }
- ext_name[n] = '\0';
-
- ignore = 0;
- if (!*c)
- more = 0;
- else {
- c++;
- if (!n)
- continue;
- }
-
- while (args && *args && *args == ' ')
- args++;
-
- /* check a client's extension against our support */
-
- ext = wsi->vhost->extensions;
-
- while (ext && ext->callback) {
-
- if (strcmp(ext_name, ext->name)) {
- ext++;
- continue;
- }
-
- /*
- * oh, we do support this one he asked for... but let's
- * confirm he only gave it once
- */
- for (m = 0; m < wsi->count_act_ext; m++)
- if (wsi->active_extensions[m] == ext) {
- lwsl_info("extension mentioned twice\n");
- return 1; /* shenanigans */
- }
-
- /*
- * ask user code if it's OK to apply it on this
- * particular connection + protocol
- */
- m = (wsi->protocol->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],
- (void *)&opts, 0)) {
- lwsl_info("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 += lws_snprintf(*p, (end - *p), "%s", ext_name);
-
- /*
- * go through the options trying to apply the
- * recognized ones
- */
-
- lwsl_debug("ext args %s", args);
-
- while (args && *args && *args != ',') {
- while (*args == ' ')
- args++;
- po = opts;
- while (po->name) {
- lwsl_debug("'%s' '%s'\n", po->name, args);
- /* only support arg-less options... */
- if (po->type == EXTARG_NONE &&
- !strncmp(args, po->name,
- strlen(po->name))) {
- oa.option_name = NULL;
- oa.option_index = po - opts;
- oa.start = NULL;
- lwsl_debug("setting %s\n", po->name);
- if (!ext->callback(
- lws_get_context(wsi), ext, wsi,
- LWS_EXT_CB_OPTION_SET,
- wsi->act_ext_user[
- wsi->count_act_ext],
- &oa, (end - *p))) {
-
- *p += lws_snprintf(*p, (end - *p), "; %s", po->name);
- lwsl_debug("adding option %s\n", po->name);
- }
- }
- po++;
- }
- while (*args && *args != ',' && *args != ';')
- args++;
- }
-
- wsi->count_act_ext++;
- lwsl_parser("count_act_ext <- %d\n",
- wsi->count_act_ext);
-
- ext++;
- }
-
- n = 0;
- args = NULL;
- }
-
- return 0;
-}
-#endif
-int
-handshake_0405(struct lws_context *context, struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct lws_process_html_args args;
- 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;
-
- /* we can only return the protocol header if:
- * - one came in, and ... */
- if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
- /* - it is not an empty string */
- wsi->protocol->name &&
- wsi->protocol->name[0]) {
- LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
- p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
- }
-
-#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.
- *
- * Give him a limited write bugdet
- */
- if (lws_extension_server_handshake(wsi, &p, 192))
- goto bail;
-#endif
- LWS_CPYAPP(p, "\x0d\x0a");
-
- args.p = p;
- args.max_len = ((char *)pt->serv_buf + context->pt_serv_buf_size) - p;
- if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
- LWS_CALLBACK_ADD_HEADERS,
- wsi->user_space, &args, 0))
- goto bail;
-
- p = args.p;
-
- /* end of response packet */
-
- LWS_CPYAPP(p, "\x0d\x0a");
-
- if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
- response, p - response)) {
-
- /* okay send the handshake response accepting the connection */
-
- lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
-#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266)
- fwrite(response, 1, p - response, stderr);
-#endif
- n = lws_write(wsi, (unsigned char *)response,
- p - response, LWS_WRITE_HTTP_HEADERS);
- if (n != (p - response)) {
- lwsl_debug("handshake_0405: ERROR writing to socket\n");
- goto bail;
- }
-
- }
-
- /* alright clean up and set ourselves into established state */
-
- wsi->state = LWSS_ESTABLISHED;
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
-
- {
- const char * uri_ptr =
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
- int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
- const struct lws_http_mount *hit =
- lws_find_mount(wsi, uri_ptr, uri_len);
- if (hit && hit->cgienv &&
- wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
- wsi->user_space, (void *)hit->cgienv, 0))
- return 1;
- }
-
- return 0;
-
-
-bail:
- /* caller will free up his parsing allocations */
- return -1;
-}
-
diff --git a/thirdparty/lws/server/server.c b/thirdparty/lws/server/server.c
deleted file mode 100644
index db05954257..0000000000
--- a/thirdparty/lws/server/server.c
+++ /dev/null
@@ -1,3025 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-const char * const method_names[] = {
- "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD",
-#ifdef LWS_WITH_HTTP2
- ":path",
-#endif
- };
-
-#if defined (LWS_WITH_ESP8266)
-#undef memcpy
-void *memcpy(void *dest, const void *src, size_t n)
-{
- return ets_memcpy(dest, src, n);
-}
-#endif
-
-int
-lws_context_init_server(struct lws_context_creation_info *info,
- struct lws_vhost *vhost)
-{
-#if LWS_POSIX
- int n, opt = 1, limit = 1;
-#endif
- lws_sockfd_type sockfd;
- struct lws_vhost *vh;
- struct lws *wsi;
- int m = 0;
-
- (void)method_names;
- (void)opt;
- /* set up our external listening socket we serve on */
-
- if (info->port == CONTEXT_PORT_NO_LISTEN ||
- info->port == CONTEXT_PORT_NO_LISTEN_SERVER)
- return 0;
-
- vh = vhost->context->vhost_list;
- while (vh) {
- if (vh->listen_port == info->port) {
- if ((!info->iface && !vh->iface) ||
- (info->iface && vh->iface &&
- !strcmp(info->iface, vh->iface))) {
- vhost->listen_port = info->port;
- vhost->iface = info->iface;
- lwsl_notice(" using listen skt from vhost %s\n",
- vh->name);
- return 0;
- }
- }
- vh = vh->vhost_next;
- }
-
-#if LWS_POSIX
- (void)n;
-#if defined(__linux__)
- limit = vhost->context->count_threads;
-#endif
-
- for (m = 0; m < limit; m++) {
-#ifdef LWS_WITH_UNIX_SOCK
- if (LWS_UNIX_SOCK_ENABLED(vhost))
- sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
- else
-#endif
-#ifdef LWS_WITH_IPV6
- if (LWS_IPV6_ENABLED(vhost))
- sockfd = socket(AF_INET6, SOCK_STREAM, 0);
- else
-#endif
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
-
- if (sockfd == -1) {
-#else
-#if defined(LWS_WITH_ESP8266)
- sockfd = esp8266_create_tcp_listen_socket(vhost);
- if (!lws_sockfd_valid(sockfd)) {
-#endif
-#endif
- lwsl_err("ERROR opening socket\n");
- return 1;
- }
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
-
-#if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE)
- /*
- * only accept that we are the only listener on the port
- * https://msdn.microsoft.com/zh-tw/library/
- * windows/desktop/ms740621(v=vs.85).aspx
- *
- * for lws, to match Linux, we default to exclusive listen
- */
- if (!lws_check_opt(vhost->options,
- LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) {
- if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
- (const void *)&opt, sizeof(opt)) < 0) {
- lwsl_err("reuseaddr failed\n");
- compatible_close(sockfd);
- return 1;
- }
- } else
-#endif
-
- /*
- * allow us to restart even if old sockets in TIME_WAIT
- */
- if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
- (const void *)&opt, sizeof(opt)) < 0) {
- lwsl_err("reuseaddr failed\n");
- compatible_close(sockfd);
- return 1;
- }
-
-#if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY)
- if (LWS_IPV6_ENABLED(vhost)) {
- if (vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) {
- int value = (vhost->options &
- LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0;
- if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
- (const void*)&value, sizeof(value)) < 0) {
- compatible_close(sockfd);
- return 1;
- }
- }
- }
-#endif
-
-#if defined(__linux__) && defined(SO_REUSEPORT)
- n = lws_check_opt(vhost->options, LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);
-#if LWS_MAX_SMP > 1
- n = 1;
-#endif
-
- if (n)
- if (vhost->context->count_threads > 1)
- if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
- (const void *)&opt, sizeof(opt)) < 0) {
- compatible_close(sockfd);
- return 1;
- }
-#endif
-#endif
- lws_plat_set_socket_options(vhost, sockfd);
-
-#if LWS_POSIX
- n = lws_socket_bind(vhost, sockfd, info->port, info->iface);
- if (n < 0)
- goto bail;
- info->port = n;
-#endif
- vhost->listen_port = info->port;
- vhost->iface = info->iface;
-
- wsi = lws_zalloc(sizeof(struct lws), "listen wsi");
- if (wsi == NULL) {
- lwsl_err("Out of mem\n");
- goto bail;
- }
- wsi->context = vhost->context;
- wsi->desc.sockfd = sockfd;
- wsi->mode = LWSCM_SERVER_LISTENER;
- wsi->protocol = vhost->protocols;
- wsi->tsi = m;
- wsi->vhost = vhost;
- wsi->listener = 1;
-
-#ifdef LWS_WITH_LIBUV
- if (LWS_LIBUV_ENABLED(vhost->context))
- lws_uv_initvhost(vhost, wsi);
-#endif
-
- if (insert_wsi_socket_into_fds(vhost->context, wsi))
- goto bail;
-
- vhost->context->count_wsi_allocated++;
- vhost->lserv_wsi = wsi;
-
-#if LWS_POSIX
- n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
- if (n < 0) {
- lwsl_err("listen failed with error %d\n", LWS_ERRNO);
- vhost->lserv_wsi = NULL;
- vhost->context->count_wsi_allocated--;
- remove_wsi_socket_from_fds(wsi);
- goto bail;
- }
- } /* for each thread able to independently listen */
-#else
-#if defined(LWS_WITH_ESP8266)
- esp8266_tcp_stream_bind(wsi->desc.sockfd, info->port, wsi);
-#endif
-#endif
- if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
-#ifdef LWS_WITH_UNIX_SOCK
- if (LWS_UNIX_SOCK_ENABLED(vhost))
- lwsl_info(" Listening on \"%s\"\n", info->iface);
- else
-#endif
- lwsl_info(" Listening on port %d\n", info->port);
- }
-
- return 0;
-
-bail:
- compatible_close(sockfd);
-
- return 1;
-}
-
-#if defined(LWS_WITH_ESP8266)
-#undef strchr
-#define strchr ets_strchr
-#endif
-
-struct lws_vhost *
-lws_select_vhost(struct lws_context *context, int port, const char *servername)
-{
- struct lws_vhost *vhost = context->vhost_list;
- const char *p;
- int n, m, colon;
-
- n = strlen(servername);
- colon = n;
- p = strchr(servername, ':');
- if (p)
- colon = p - servername;
-
- /* Priotity 1: first try exact matches */
-
- while (vhost) {
- if (port == vhost->listen_port &&
- !strncmp(vhost->name, servername, colon)) {
- lwsl_info("SNI: Found: %s\n", servername);
- return vhost;
- }
- vhost = vhost->vhost_next;
- }
-
- /*
- * Priority 2: if no exact matches, try matching *.vhost-name
- * unintentional matches are possible but resolve to x.com for *.x.com
- * which is reasonable. If exact match exists we already chose it and
- * never reach here. SSL will still fail it if the cert doesn't allow
- * *.x.com.
- */
- vhost = context->vhost_list;
- while (vhost) {
- m = strlen(vhost->name);
- if (port == vhost->listen_port &&
- m <= (colon - 2) &&
- servername[colon - m - 1] == '.' &&
- !strncmp(vhost->name, servername + colon - m, m)) {
- lwsl_info("SNI: Found %s on wildcard: %s\n",
- servername, vhost->name);
- return vhost;
- }
- vhost = vhost->vhost_next;
- }
-
- /* Priority 3: match the first vhost on our port */
-
- vhost = context->vhost_list;
- while (vhost) {
- if (port == vhost->listen_port) {
- lwsl_info("vhost match to %s based on port %d\n",
- vhost->name, port);
- return vhost;
- }
- vhost = vhost->vhost_next;
- }
-
- /* no match */
-
- return NULL;
-}
-
-LWS_VISIBLE LWS_EXTERN const char *
-lws_get_mimetype(const char *file, const struct lws_http_mount *m)
-{
- int n = strlen(file);
- const struct lws_protocol_vhost_options *pvo = NULL;
-
- if (m)
- pvo = m->extra_mimetypes;
-
- if (n < 5)
- return NULL;
-
- if (!strcmp(&file[n - 4], ".ico"))
- return "image/x-icon";
-
- if (!strcmp(&file[n - 4], ".gif"))
- return "image/gif";
-
- if (!strcmp(&file[n - 3], ".js"))
- return "text/javascript";
-
- if (!strcmp(&file[n - 4], ".png"))
- return "image/png";
-
- if (!strcmp(&file[n - 4], ".jpg"))
- return "image/jpeg";
-
- if (!strcmp(&file[n - 3], ".gz"))
- return "application/gzip";
-
- if (!strcmp(&file[n - 4], ".JPG"))
- return "image/jpeg";
-
- if (!strcmp(&file[n - 5], ".html"))
- return "text/html";
-
- if (!strcmp(&file[n - 4], ".css"))
- return "text/css";
-
- if (!strcmp(&file[n - 4], ".txt"))
- return "text/plain";
-
- if (!strcmp(&file[n - 4], ".svg"))
- return "image/svg+xml";
-
- if (!strcmp(&file[n - 4], ".ttf"))
- return "application/x-font-ttf";
-
- if (!strcmp(&file[n - 4], ".otf"))
- return "application/font-woff";
-
- if (!strcmp(&file[n - 5], ".woff"))
- return "application/font-woff";
-
- if (!strcmp(&file[n - 4], ".xml"))
- return "application/xml";
-
- while (pvo) {
- if (pvo->name[0] == '*') /* ie, match anything */
- return pvo->value;
-
- if (!strcmp(&file[n - strlen(pvo->name)], pvo->name))
- return pvo->value;
-
- pvo = pvo->next;
- }
-
- return NULL;
-}
-static lws_fop_flags_t
-lws_vfs_prepare_flags(struct lws *wsi)
-{
- lws_fop_flags_t f = 0;
-
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))
- return f;
-
- if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING),
- "gzip")) {
- lwsl_info("client indicates GZIP is acceptable\n");
- f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;
- }
-
- return f;
-}
-
-static int
-lws_http_serve(struct lws *wsi, char *uri, const char *origin,
- const struct lws_http_mount *m)
-{
- const struct lws_protocol_vhost_options *pvo = m->interpret;
- struct lws_process_html_args args;
- const char *mimetype;
-#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266)
- const struct lws_plat_file_ops *fops;
- const char *vpath;
- lws_fop_flags_t fflags = LWS_O_RDONLY;
-#if defined(WIN32) && defined(LWS_HAVE__STAT32I64)
- struct _stat32i64 st;
-#else
- struct stat st;
-#endif
- int spin = 0;
-#endif
- char path[256], sym[512];
- unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
- unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
-#if !defined(WIN32) && LWS_POSIX && !defined(LWS_WITH_ESP32)
- size_t len;
-#endif
- int n;
-
- lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
-
-#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266)
-
- fflags |= lws_vfs_prepare_flags(wsi);
-
- do {
- spin++;
- fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath);
-
- if (wsi->u.http.fop_fd)
- lws_vfs_file_close(&wsi->u.http.fop_fd);
-
- wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
- path, vpath, &fflags);
- if (!wsi->u.http.fop_fd) {
- lwsl_err("Unable to open '%s'\n", path);
-
- return -1;
- }
-
- /* if it can't be statted, don't try */
- if (fflags & LWS_FOP_FLAG_VIRTUAL)
- break;
-#if defined(LWS_WITH_ESP32)
- break;
-#endif
-#if !defined(WIN32)
- if (fstat(wsi->u.http.fop_fd->fd, &st)) {
- lwsl_info("unable to stat %s\n", path);
- goto bail;
- }
-#else
-#if defined(LWS_HAVE__STAT32I64)
- if (_stat32i64(path, &st)) {
- lwsl_info("unable to stat %s\n", path);
- goto bail;
- }
-#else
- if (stat(path, &st)) {
- lwsl_info("unable to stat %s\n", path);
- goto bail;
- }
-#endif
-#endif
-
- wsi->u.http.fop_fd->mod_time = (uint32_t)st.st_mtime;
- fflags |= LWS_FOP_FLAG_MOD_TIME_VALID;
-
-#if !defined(WIN32) && LWS_POSIX && !defined(LWS_WITH_ESP32)
- if ((S_IFMT & st.st_mode) == S_IFLNK) {
- len = readlink(path, sym, sizeof(sym) - 1);
- if (len) {
- lwsl_err("Failed to read link %s\n", path);
- goto bail;
- }
- sym[len] = '\0';
- lwsl_debug("symlink %s -> %s\n", path, sym);
- lws_snprintf(path, sizeof(path) - 1, "%s", sym);
- }
-#endif
- if ((S_IFMT & st.st_mode) == S_IFDIR) {
- lwsl_debug("default filename append to dir\n");
- lws_snprintf(path, sizeof(path) - 1, "%s/%s/index.html",
- origin, uri);
- }
-
- } while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
-
- if (spin == 5)
- lwsl_err("symlink loop %s \n", path);
-
- n = sprintf(sym, "%08llX%08lX",
- (unsigned long long)lws_vfs_get_length(wsi->u.http.fop_fd),
- (unsigned long)lws_vfs_get_mod_time(wsi->u.http.fop_fd));
-
- /* disable ranges if IF_RANGE token invalid */
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE))
- if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE)))
- /* differs - defeat Range: */
- wsi->u.http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
- /*
- * he thinks he has some version of it already,
- * check if the tag matches
- */
- if (!strcmp(sym, lws_hdr_simple_ptr(wsi,
- WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
-
- lwsl_debug("%s: ETAG match %s %s\n", __func__,
- uri, origin);
-
- /* we don't need to send the payload */
- if (lws_add_http_header_status(wsi,
- HTTP_STATUS_NOT_MODIFIED, &p, end))
- return -1;
-
- if (lws_add_http_header_by_token(wsi,
- WSI_TOKEN_HTTP_ETAG,
- (unsigned char *)sym, n, &p, end))
- return -1;
-
- if (lws_finalize_http_header(wsi, &p, end))
- return -1;
-
- n = lws_write(wsi, start, p - start,
- LWS_WRITE_HTTP_HEADERS |
- LWS_WRITE_H2_STREAM_END);
- if (n != (p - start)) {
- lwsl_err("_write returned %d from %ld\n", n,
- (long)(p - start));
- return -1;
- }
-
- lws_vfs_file_close(&wsi->u.http.fop_fd);
-
- return lws_http_transaction_completed(wsi);
- }
- }
-
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,
- (unsigned char *)sym, n, &p, end))
- return -1;
-#endif
-
- mimetype = lws_get_mimetype(path, m);
- if (!mimetype) {
- lwsl_err("unknown mimetype for %s\n", path);
- goto bail;
- }
- if (!mimetype[0])
- lwsl_debug("sending no mimetype for %s\n", path);
-
- wsi->sending_chunked = 0;
-
- /*
- * check if this is in the list of file suffixes to be interpreted by
- * a protocol
- */
- while (pvo) {
- n = strlen(path);
- if (n > (int)strlen(pvo->name) &&
- !strcmp(&path[n - strlen(pvo->name)], pvo->name)) {
- wsi->sending_chunked = 1;
- wsi->protocol_interpret_idx =
- (char)(lws_intptr_t)pvo->value;
- lwsl_info("want %s interpreted by %s\n", path,
- wsi->vhost->protocols[
- (int)(lws_intptr_t)(pvo->value)].name);
- wsi->protocol = &wsi->vhost->protocols[
- (int)(lws_intptr_t)(pvo->value)];
- if (lws_ensure_user_space(wsi))
- return -1;
- break;
- }
- pvo = pvo->next;
- }
-
- if (m->protocol) {
- const struct lws_protocols *pp = lws_vhost_name_to_protocol(
- wsi->vhost, m->protocol);
-
- if (lws_bind_protocol(wsi, pp))
- return 1;
- args.p = (char *)p;
- args.max_len = end - p;
- if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
- wsi->user_space, &args, 0))
- return -1;
- p = (unsigned char *)args.p;
- }
-
- n = lws_serve_http_file(wsi, path, mimetype, (char *)start, p - start);
-
- if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
- return -1; /* error or can't reuse connection: close the socket */
-
- return 0;
-bail:
-
- return -1;
-}
-
-const struct lws_http_mount *
-lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
-{
- const struct lws_http_mount *hm, *hit = NULL;
- int best = 0;
-
- hm = wsi->vhost->mount_list;
- while (hm) {
- if (uri_len >= hm->mountpoint_len &&
- !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) &&
- (uri_ptr[hm->mountpoint_len] == '\0' ||
- uri_ptr[hm->mountpoint_len] == '/' ||
- hm->mountpoint_len == 1)
- ) {
- if (hm->origin_protocol == LWSMPRO_CALLBACK ||
- ((hm->origin_protocol == LWSMPRO_CGI ||
- lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) ||
- (wsi->http2_substream &&
- lws_hdr_total_length(wsi,
- WSI_TOKEN_HTTP_COLON_PATH)) ||
- hm->protocol) &&
- hm->mountpoint_len > best)) {
- best = hm->mountpoint_len;
- hit = hm;
- }
- }
- hm = hm->mount_next;
- }
-
- return hit;
-}
-
-#if LWS_POSIX
-
-static int
-lws_find_string_in_file(const char *filename, const char *string, int stringlen)
-{
- char buf[128];
- int fd, match = 0, pos = 0, n = 0, hit = 0;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0) {
- lwsl_err("can't open auth file: %s\n", filename);
- return 1;
- }
-
- while (1) {
- if (pos == n) {
- n = read(fd, buf, sizeof(buf));
- if (n <= 0) {
- if (match == stringlen)
- hit = 1;
- break;
- }
- pos = 0;
- }
-
- if (match == stringlen) {
- if (buf[pos] == '\r' || buf[pos] == '\n') {
- hit = 1;
- break;
- }
- match = 0;
- }
-
- if (buf[pos] == string[match])
- match++;
- else
- match = 0;
-
- pos++;
- }
-
- close(fd);
-
- return hit;
-}
-
-static int
-lws_unauthorised_basic_auth(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- unsigned char *start = pt->serv_buf + LWS_PRE,
- *p = start, *end = p + 512;
- char buf[64];
- int n;
-
- /* no auth... tell him it is required */
-
- if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end))
- return -1;
-
- n = lws_snprintf(buf, sizeof(buf), "Basic realm=\"lwsws\"");
- if (lws_add_http_header_by_token(wsi,
- WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
- (unsigned char *)buf, n, &p, end))
- return -1;
-
- if (lws_finalize_http_header(wsi, &p, end))
- return -1;
-
- n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS |
- LWS_WRITE_H2_STREAM_END);
- if (n < 0)
- return -1;
-
- return lws_http_transaction_completed(wsi);
-
-}
-
-#endif
-
-int lws_clean_url(char *p)
-{
- if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') {
- p += 4;
- if (*p == 's')
- p++;
- if (*p == ':') {
- p++;
- if (*p == '/')
- p++;
- }
- }
-
- while (*p) {
- if (p[0] == '/' && p[1] == '/') {
- char *p1 = p;
- while (*p1) {
- *p1 = p1[1];
- p1++;
- }
- continue;
- }
- p++;
- }
-
- return 0;
-}
-
-
-static const unsigned char methods[] = {
- WSI_TOKEN_GET_URI,
- WSI_TOKEN_POST_URI,
- WSI_TOKEN_OPTIONS_URI,
- WSI_TOKEN_PUT_URI,
- WSI_TOKEN_PATCH_URI,
- WSI_TOKEN_DELETE_URI,
- WSI_TOKEN_CONNECT,
- WSI_TOKEN_HEAD_URI,
-#ifdef LWS_WITH_HTTP2
- WSI_TOKEN_HTTP_COLON_PATH,
-#endif
-};
-
-static int
-lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
-{
- int n, count = 0;
-
- for (n = 0; n < ARRAY_SIZE(methods); n++)
- if (lws_hdr_total_length(wsi, methods[n]))
- count++;
- if (!count) {
- lwsl_warn("Missing URI in HTTP request\n");
- return -1;
- }
-
- if (count != 1 &&
- !(wsi->http2_substream &&
- lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH))) {
- lwsl_warn("multiple methods?\n");
- return -1;
- }
-
- for (n = 0; n < ARRAY_SIZE(methods); n++)
- if (lws_hdr_total_length(wsi, methods[n])) {
- *puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
- *puri_len = lws_hdr_total_length(wsi, methods[n]);
- return n;
- }
-
- return -1;
-}
-
-int
-lws_http_action(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- enum http_connection_type connection_type;
- enum http_version request_version;
- char content_length_str[32];
- struct lws_process_html_args args;
- const struct lws_http_mount *hit = NULL;
- unsigned int n;
- char http_version_str[10];
- char http_conn_str[20];
- int http_version_len;
- char *uri_ptr = NULL, *s;
- int uri_len = 0, meth;
- static const char * const oprot[] = {
- "http://", "https://"
- };
-
- meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
- if (meth < 0 || meth >= ARRAY_SIZE(method_names))
- goto bail_nuke_ah;
-
- /* we insist on absolute paths */
-
- if (!uri_ptr || uri_ptr[0] != '/') {
- lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
-
- goto bail_nuke_ah;
- }
-
- lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth],
- meth, uri_ptr);
-
- if (lws_ensure_user_space(wsi))
- goto bail_nuke_ah;
-
- /* HTTP header had a content length? */
-
- wsi->u.http.rx_content_length = 0;
- if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
- lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
- lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
- wsi->u.http.rx_content_length = 100 * 1024 * 1024;
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
- lws_hdr_copy(wsi, content_length_str,
- sizeof(content_length_str) - 1,
- WSI_TOKEN_HTTP_CONTENT_LENGTH);
- wsi->u.http.rx_content_length = atoll(content_length_str);
- }
-
- if (wsi->http2_substream) {
- wsi->u.http.request_version = HTTP_VERSION_2;
- } else {
- /* http_version? Default to 1.0, override with token: */
- request_version = HTTP_VERSION_1_0;
-
- /* Works for single digit HTTP versions. : */
- http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
- if (http_version_len > 7) {
- lws_hdr_copy(wsi, http_version_str,
- sizeof(http_version_str) - 1,
- WSI_TOKEN_HTTP);
- if (http_version_str[5] == '1' &&
- http_version_str[7] == '1')
- request_version = HTTP_VERSION_1_1;
- }
- wsi->u.http.request_version = request_version;
-
- /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
- if (request_version == HTTP_VERSION_1_1)
- connection_type = HTTP_CONNECTION_KEEP_ALIVE;
- else
- connection_type = HTTP_CONNECTION_CLOSE;
-
- /* Override default if http "Connection:" header: */
- if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
- lws_hdr_copy(wsi, http_conn_str,
- sizeof(http_conn_str) - 1,
- WSI_TOKEN_CONNECTION);
- http_conn_str[sizeof(http_conn_str) - 1] = '\0';
- if (!strcasecmp(http_conn_str, "keep-alive"))
- connection_type = HTTP_CONNECTION_KEEP_ALIVE;
- else
- if (!strcasecmp(http_conn_str, "close"))
- connection_type = HTTP_CONNECTION_CLOSE;
- }
- wsi->u.http.connection_type = connection_type;
- }
-
- n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
- wsi->user_space, uri_ptr, uri_len);
- if (n) {
- lwsl_info("LWS_CALLBACK_HTTP closing\n");
-
- return 1;
- }
- /*
- * if there is content supposed to be coming,
- * put a timeout on it having arrived
- */
- lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
- wsi->context->timeout_secs);
-#ifdef LWS_OPENSSL_SUPPORT
- if (wsi->redirect_to_https) {
- /*
- * we accepted http:// only so we could redirect to
- * https://, so issue the redirect. Create the redirection
- * URI from the host: header and ignore the path part
- */
- unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
- *end = p + 512;
-
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
- goto bail_nuke_ah;
-
- n = sprintf((char *)end, "https://%s/",
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
-
- n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
- end, n, &p, end);
- if ((int)n < 0)
- goto bail_nuke_ah;
-
- return lws_http_transaction_completed(wsi);
- }
-#endif
-
-#ifdef LWS_WITH_ACCESS_LOG
- lws_prepare_access_log_info(wsi, uri_ptr, meth);
-#endif
-
- /* can we serve it from the mount list? */
-
- hit = lws_find_mount(wsi, uri_ptr, uri_len);
- if (!hit) {
- /* deferred cleanup and reset to protocols[0] */
-
- lwsl_info("no hit\n");
-
- if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0]))
- return 1;
-
- n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
- wsi->user_space, uri_ptr, uri_len);
-
- goto after;
- }
-
- s = uri_ptr + hit->mountpoint_len;
-
- /*
- * if we have a mountpoint like https://xxx.com/yyy
- * there is an implied / at the end for our purposes since
- * we can only mount on a "directory".
- *
- * But if we just go with that, the browser cannot understand
- * that he is actually looking down one "directory level", so
- * even though we give him /yyy/abc.html he acts like the
- * current directory level is /. So relative urls like "x.png"
- * wrongly look outside the mountpoint.
- *
- * Therefore if we didn't come in on a url with an explicit
- * / at the end, we must redirect to add it so the browser
- * understands he is one "directory level" down.
- */
- if ((hit->mountpoint_len > 1 ||
- (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
- hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
- (*s != '/' ||
- (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
- hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
- (hit->origin_protocol != LWSMPRO_CGI &&
- hit->origin_protocol != LWSMPRO_CALLBACK)) {
- unsigned char *start = pt->serv_buf + LWS_PRE,
- *p = start, *end = p + 512;
-
- lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin);
-
- /* > at start indicates deal with by redirect */
- if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
- hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
- n = lws_snprintf((char *)end, 256, "%s%s",
- oprot[hit->origin_protocol & 1],
- hit->origin);
- else {
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
- if (!lws_hdr_total_length(wsi,
- WSI_TOKEN_HTTP_COLON_AUTHORITY))
- goto bail_nuke_ah;
- n = lws_snprintf((char *)end, 256,
- "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
- lws_hdr_simple_ptr(wsi,
- WSI_TOKEN_HTTP_COLON_AUTHORITY),
- uri_ptr);
- } else
- n = lws_snprintf((char *)end, 256,
- "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
- uri_ptr);
- }
-
- lws_clean_url((char *)end);
- n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
- end, n, &p, end);
- if ((int)n < 0)
- goto bail_nuke_ah;
-
- return lws_http_transaction_completed(wsi);
- }
-
-#if LWS_POSIX
- /* basic auth? */
-
- if (hit->basic_auth_login_file) {
- char b64[160], plain[(sizeof(b64) * 3) / 4];
- int m;
-
- /* Did he send auth? */
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION))
- return lws_unauthorised_basic_auth(wsi);
-
- n = HTTP_STATUS_FORBIDDEN;
-
- m = lws_hdr_copy(wsi, b64, sizeof(b64),
- WSI_TOKEN_HTTP_AUTHORIZATION);
- if (m < 7) {
- lwsl_err("b64 auth too long\n");
- goto transaction_result_n;
- }
-
- b64[5] = '\0';
- if (strcasecmp(b64, "Basic")) {
- lwsl_err("auth missing basic: %s\n", b64);
- goto transaction_result_n;
- }
-
- /* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */
-
- m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain));
- if (m < 0) {
- lwsl_err("plain auth too long\n");
- goto transaction_result_n;
- }
-
- if (!lws_find_string_in_file(hit->basic_auth_login_file,
- plain, m)) {
- lwsl_err("basic auth lookup failed\n");
- return lws_unauthorised_basic_auth(wsi);
- }
-
- lwsl_notice("basic auth accepted\n");
-
- /* accept the auth */
- }
-#endif
-
-#if defined(LWS_WITH_HTTP_PROXY)
- /*
- * The mount is a reverse proxy?
- */
-
- if (hit->origin_protocol == LWSMPRO_HTTPS ||
- hit->origin_protocol == LWSMPRO_HTTP) {
- struct lws_client_connect_info i;
- char ads[96], rpath[256], *pcolon, *pslash, *p;
- int n, na;
-
- memset(&i, 0, sizeof(i));
- i.context = lws_get_context(wsi);
-
- pcolon = strchr(hit->origin, ':');
- pslash = strchr(hit->origin, '/');
- if (!pslash) {
- lwsl_err("Proxy mount origin '%s' must have /\n",
- hit->origin);
- return -1;
- }
- if (pcolon > pslash)
- pcolon = NULL;
-
- if (pcolon)
- n = pcolon - hit->origin;
- else
- n = pslash - hit->origin;
-
- if (n >= sizeof(ads) - 2)
- n = sizeof(ads) - 2;
-
- memcpy(ads, hit->origin, n);
- ads[n] = '\0';
-
- i.address = ads;
- i.port = 80;
- if (hit->origin_protocol == LWSMPRO_HTTPS) {
- i.port = 443;
- i.ssl_connection = 1;
- }
- if (pcolon)
- i.port = atoi(pcolon + 1);
-
- lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s", pslash + 1,
- uri_ptr + hit->mountpoint_len);
- lws_clean_url(rpath);
- na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
- if (na) {
- p = rpath + strlen(rpath);
- *p++ = '?';
- lws_hdr_copy(wsi, p, &rpath[sizeof(rpath) - 1] - p,
- WSI_TOKEN_HTTP_URI_ARGS);
- while (--na) {
- if (*p == '\0')
- *p = '&';
- p++;
- }
- }
-
-
- i.path = rpath;
- i.host = i.address;
- i.origin = NULL;
- i.method = "GET";
- i.parent_wsi = wsi;
- i.uri_replace_from = hit->origin;
- i.uri_replace_to = hit->mountpoint;
-
- lwsl_notice("proxying to %s port %d url %s, ssl %d, from %s, to %s\n",
- i.address, i.port, i.path, i.ssl_connection,
- i.uri_replace_from, i.uri_replace_to);
-
- if (!lws_client_connect_via_info(&i)) {
- lwsl_err("proxy connect fail\n");
- return 1;
- }
-
- return 0;
- }
-#endif
-
- /*
- * A particular protocol callback is mounted here?
- *
- * For the duration of this http transaction, bind us to the
- * associated protocol
- */
- if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) {
- const struct lws_protocols *pp;
- const char *name = hit->origin;
- if (hit->protocol)
- name = hit->protocol;
-
- pp = lws_vhost_name_to_protocol(wsi->vhost, name);
- if (!pp) {
- n = -1;
- lwsl_err("Unable to find plugin '%s'\n",
- hit->origin);
- return 1;
- }
-
- if (lws_bind_protocol(wsi, pp))
- return 1;
-
- args.p = uri_ptr;
- args.len = uri_len;
- args.max_len = hit->auth_mask;
- args.final = 0; /* used to signal callback dealt with it */
-
- n = wsi->protocol->callback(wsi, LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
- wsi->user_space, &args, 0);
- if (n) {
- lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
- NULL);
- goto bail_nuke_ah;
- }
- if (args.final) /* callback completely handled it well */
- return 0;
-
- if (hit->cgienv && wsi->protocol->callback(wsi,
- LWS_CALLBACK_HTTP_PMO,
- wsi->user_space, (void *)hit->cgienv, 0))
- return 1;
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
- n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
- wsi->user_space,
- uri_ptr + hit->mountpoint_len,
- uri_len - hit->mountpoint_len);
- goto after;
- }
- }
-
-#ifdef LWS_WITH_CGI
- /* did we hit something with a cgi:// origin? */
- if (hit->origin_protocol == LWSMPRO_CGI) {
- const char *cmd[] = {
- NULL, /* replace with cgi path */
- NULL
- };
-
- lwsl_debug("%s: cgi\n", __func__);
- cmd[0] = hit->origin;
-
- n = 5;
- if (hit->cgi_timeout)
- n = hit->cgi_timeout;
-
- n = lws_cgi(wsi, cmd, hit->mountpoint_len, n,
- hit->cgienv);
- if (n) {
- lwsl_err("%s: cgi failed\n", __func__);
- return -1;
- }
-
- goto deal_body;
- }
-#endif
-
- n = strlen(s);
- if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
- s = (char *)hit->def;
- if (!s)
- s = "index.html";
-
- wsi->cache_secs = hit->cache_max_age;
- wsi->cache_reuse = hit->cache_reusable;
- wsi->cache_revalidate = hit->cache_revalidate;
- wsi->cache_intermediaries = hit->cache_intermediaries;
-
- n = lws_http_serve(wsi, s, hit->origin, hit);
- if (n) {
- /*
- * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
- */
- if (hit->protocol) {
- const struct lws_protocols *pp = lws_vhost_name_to_protocol(
- wsi->vhost, hit->protocol);
-
- if (lws_bind_protocol(wsi, pp))
- return 1;
-
- n = pp->callback(wsi, LWS_CALLBACK_HTTP,
- wsi->user_space,
- uri_ptr + hit->mountpoint_len,
- uri_len - hit->mountpoint_len);
- } else
- n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
- wsi->user_space, uri_ptr, uri_len);
- }
-
-after:
- if (n) {
- lwsl_info("LWS_CALLBACK_HTTP closing\n");
-
- return 1;
- }
-
-#ifdef LWS_WITH_CGI
-deal_body:
-#endif
- /*
- * If we're not issuing a file, check for content_length or
- * HTTP keep-alive. No keep-alive header allocation for
- * ISSUING_FILE, as this uses HTTP/1.0.
- *
- * In any case, return 0 and let lws_read decide how to
- * proceed based on state
- */
- if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
- /* Prepare to read body if we have a content length: */
- lwsl_debug("wsi->u.http.rx_content_length %lld %d %d\n",
- (long long)wsi->u.http.rx_content_length,
- wsi->upgraded_to_http2, wsi->http2_substream);
- if (wsi->u.http.rx_content_length > 0) {
- lwsl_notice("%s: %p: LWSS_HTTP_BODY state set\n",
- __func__, wsi);
- wsi->state = LWSS_HTTP_BODY;
- wsi->u.http.rx_content_remain =
- wsi->u.http.rx_content_length;
- }
- }
-
- return 0;
-
-bail_nuke_ah:
- /* we're closing, losing some rx is OK */
- lws_header_table_force_to_detachable_state(wsi);
- lws_header_table_detach(wsi, 1);
-
- return 1;
-
-#if LWS_POSIX
-transaction_result_n:
- lws_return_http_status(wsi, n, NULL);
-
- return lws_http_transaction_completed(wsi);
-#endif
-}
-
-static int
-lws_server_init_wsi_for_ws(struct lws *wsi)
-{
- int n;
-
- wsi->state = LWSS_ESTABLISHED;
- lws_restart_ws_ping_pong_timer(wsi);
-
- /*
- * create the frame buffer for this connection according to the
- * size mentioned in the protocol definition. If 0 there, use
- * a big default for compatibility
- */
-
- n = wsi->protocol->rx_buffer_size;
- if (!n)
- n = wsi->context->pt_serv_buf_size;
- n += LWS_PRE;
- wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf");
- if (!wsi->u.ws.rx_ubuf) {
- lwsl_err("Out of Mem allocating rx buffer %d\n", n);
- return 1;
- }
- wsi->u.ws.rx_ubuf_alloc = n;
- lwsl_debug("Allocating RX buffer %d\n", n);
-
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
- if (!wsi->parent_carries_io)
- if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
- (const char *)&n, sizeof n)) {
- lwsl_warn("Failed to set SNDBUF to %d", n);
- return 1;
- }
-#endif
-
- /* notify user code that we're ready to roll */
-
- if (wsi->protocol->callback)
- if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
- wsi->user_space,
-#ifdef LWS_OPENSSL_SUPPORT
- wsi->ssl,
-#else
- NULL,
-#endif
- 0))
- return 1;
-
- return 0;
-}
-
-int
-lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
-{
- int protocol_len, n = 0, hit, non_space_char_found = 0, m;
- struct lws_context *context = lws_get_context(wsi);
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct _lws_header_related hdr;
- struct allocated_headers *ah;
- unsigned char *obuf = *buf;
- char protocol_list[128];
- char protocol_name[64];
- size_t olen = len;
- char *p;
-
- if (len >= 10000000) {
- lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
- assert(0);
- }
-
- if (!wsi->u.hdr.ah) {
- lwsl_err("%s: assert: NULL ah\n", __func__);
- assert(0);
- }
-
- lwsl_hexdump(*buf, len);
-
- while (len--) {
- wsi->more_rx_waiting = !!len;
-
- if (wsi->mode != LWSCM_HTTP_SERVING &&
- wsi->mode != LWSCM_HTTP2_SERVING &&
- wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) {
- lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode);
- goto bail_nuke_ah;
- }
-
- m = lws_parse(wsi, *(*buf)++);
- if (m) {
- if (m == 2) {
- /*
- * we are transitioning from http with
- * an AH, to raw. Drop the ah and set
- * the mode.
- */
-raw_transition:
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- lws_bind_protocol(wsi, &wsi->vhost->protocols[
- wsi->vhost->
- raw_protocol_index]);
- lwsl_info("transition to raw vh %s prot %d\n",
- wsi->vhost->name,
- wsi->vhost->raw_protocol_index);
- if ((wsi->protocol->callback)(wsi,
- LWS_CALLBACK_RAW_ADOPT,
- wsi->user_space, NULL, 0))
- goto bail_nuke_ah;
-
- lws_header_table_force_to_detachable_state(wsi);
- lws_union_transition(wsi, LWSCM_RAW);
- lws_header_table_detach(wsi, 1);
-
- if (m == 2 && (wsi->protocol->callback)(wsi,
- LWS_CALLBACK_RAW_RX,
- wsi->user_space, obuf, olen))
- return 1;
-
- return 0;
- }
- lwsl_info("lws_parse failed\n");
- goto bail_nuke_ah;
- }
-
- if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
- continue;
-
- lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
- lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
- wsi->more_rx_waiting);
-
- /* select vhost */
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
- struct lws_vhost *vhost = lws_select_vhost(
- context, wsi->vhost->listen_port,
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
-
- if (vhost)
- wsi->vhost = vhost;
- } else
- lwsl_info("no host\n");
-
- if (wsi->mode != LWSCM_HTTP2_SERVING) {
- wsi->vhost->conn_stats.h1_trans++;
- if (!wsi->conn_stat_done) {
- wsi->vhost->conn_stats.h1_conn++;
- wsi->conn_stat_done = 1;
- }
- }
-
- /* check for unwelcome guests */
-
- if (wsi->context->reject_service_keywords) {
- const struct lws_protocol_vhost_options *rej =
- wsi->context->reject_service_keywords;
- char ua[384], *msg = NULL;
-
- if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1,
- WSI_TOKEN_HTTP_USER_AGENT) > 0) {
- ua[sizeof(ua) - 1] = '\0';
- while (rej) {
- if (strstr(ua, rej->name)) {
-#ifdef LWS_WITH_ACCESS_LOG
- char *uri_ptr = NULL;
- int meth, uri_len;
-#endif
-
- msg = strchr(rej->value, ' ');
- if (msg)
- msg++;
- lws_return_http_status(wsi,
- atoi(rej->value), msg);
-#ifdef LWS_WITH_ACCESS_LOG
- meth = lws_http_get_uri_and_method(wsi,
- &uri_ptr, &uri_len);
- if (meth >= 0)
- lws_prepare_access_log_info(wsi,
- uri_ptr, meth);
-
- /* wsi close will do the log */
-#endif
- wsi->vhost->conn_stats.rejected++;
- /*
- * We don't want anything from
- * this rejected guy. Follow
- * the close flow, not the
- * transaction complete flow.
- */
- goto bail_nuke_ah;
- }
- rej = rej->next;
- }
- }
- }
-
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {
- lwsl_info("Changing to RAW mode\n");
- m = 0;
- goto raw_transition;
- }
-
- wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- /* is this websocket protocol or normal http 1.0? */
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
- if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
- "websocket")) {
- wsi->vhost->conn_stats.ws_upg++;
- lwsl_info("Upgrade to ws\n");
- goto upgrade_ws;
- }
-#ifdef LWS_WITH_HTTP2
- if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
- "h2c")) {
- wsi->vhost->conn_stats.h2_upg++;
- lwsl_info("Upgrade to h2c\n");
- goto upgrade_h2c;
- }
-#endif
- lwsl_info("Unknown upgrade\n");
- /* dunno what he wanted to upgrade to */
- goto bail_nuke_ah;
- }
-
- /* no upgrade ack... he remained as HTTP */
-
- lwsl_info("No upgrade\n");
- ah = wsi->u.hdr.ah;
-
- lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
- wsi->state = LWSS_HTTP;
- wsi->u.http.fop_fd = NULL;
-
- /* expose it at the same offset as u.hdr */
- wsi->u.http.ah = ah;
- lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
- (void *)wsi->u.hdr.ah);
-
- n = lws_http_action(wsi);
-
- return n;
-
-#ifdef LWS_WITH_HTTP2
-upgrade_h2c:
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
- lwsl_info("missing http2_settings\n");
- goto bail_nuke_ah;
- }
-
- lwsl_info("h2c upgrade...\n");
-
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
- /* convert the peer's HTTP-Settings */
- n = lws_b64_decode_string(p, protocol_list,
- sizeof(protocol_list));
- if (n < 0) {
- lwsl_parser("HTTP2_SETTINGS too long\n");
- return 1;
- }
-
- /* adopt the header info */
-
- ah = wsi->u.hdr.ah;
-
- lws_union_transition(wsi, LWSCM_HTTP2_SERVING);
-
- /* http2 union member has http union struct at start */
- wsi->u.http.ah = ah;
-
- if (!wsi->u.h2.h2n) {
- wsi->u.h2.h2n = lws_zalloc(sizeof(*wsi->u.h2.h2n), "h2n");
- if (!wsi->u.h2.h2n)
- return 1;
- }
-
- lws_h2_init(wsi);
-
- /* HTTP2 union */
-
- lws_h2_settings(wsi, &wsi->u.h2.h2n->set,
- (unsigned char *)protocol_list, n);
-
- lws_hpack_dynamic_size(wsi, wsi->u.h2.h2n->set.s[
- H2SET_HEADER_TABLE_SIZE]);
-
- strcpy(protocol_list, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
- "Connection: Upgrade\x0d\x0a"
- "Upgrade: h2c\x0d\x0a\x0d\x0a");
- n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
- strlen(protocol_list));
- if (n != strlen(protocol_list)) {
- lwsl_debug("http2 switch: ERROR writing to socket\n");
- return 1;
- }
-
- wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE;
-
- return 0;
-#endif
-
-upgrade_ws:
- if (!wsi->protocol)
- lwsl_err("NULL protocol at lws_read\n");
-
- /*
- * It's websocket
- *
- * Select the first protocol we support from the list
- * the client sent us.
- *
- * Copy it to remove header fragmentation
- */
-
- if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
- WSI_TOKEN_PROTOCOL) < 0) {
- lwsl_err("protocol list too long");
- goto bail_nuke_ah;
- }
-
- protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
- protocol_list[protocol_len] = '\0';
- p = protocol_list;
- hit = 0;
-
- while (*p && !hit) {
- n = 0;
- non_space_char_found = 0;
- while (n < sizeof(protocol_name) - 1 &&
- *p && *p != ',') {
- /* ignore leading spaces */
- if (!non_space_char_found && *p == ' ') {
- n++;
- continue;
- }
- non_space_char_found = 1;
- protocol_name[n++] = *p++;
- }
- protocol_name[n] = '\0';
- if (*p)
- p++;
-
- lwsl_info("checking %s\n", protocol_name);
-
- n = 0;
- while (wsi->vhost->protocols[n].callback) {
- lwsl_info("try %s\n",
- wsi->vhost->protocols[n].name);
-
- if (wsi->vhost->protocols[n].name &&
- !strcmp(wsi->vhost->protocols[n].name,
- protocol_name)) {
- wsi->protocol = &wsi->vhost->protocols[n];
- hit = 1;
- break;
- }
-
- n++;
- }
- }
-
- /* we didn't find a protocol he wanted? */
-
- if (!hit) {
- if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
- lwsl_info("No protocol from \"%s\" supported\n",
- protocol_list);
- goto bail_nuke_ah;
- }
- /*
- * some clients only have one protocol and
- * do not send the protocol list header...
- * allow it and match to the vhost's default
- * protocol (which itself defaults to zero)
- */
- lwsl_info("defaulting to prot handler %d\n",
- wsi->vhost->default_protocol_index);
- n = wsi->vhost->default_protocol_index;
- wsi->protocol = &wsi->vhost->protocols[
- (int)wsi->vhost->default_protocol_index];
- }
-
- /* allocate wsi->user storage */
- if (lws_ensure_user_space(wsi))
- goto bail_nuke_ah;
-
- /*
- * Give the user code a chance to study the request and
- * have the opportunity to deny it
- */
- if ((wsi->protocol->callback)(wsi,
- LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
- wsi->user_space,
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
- lwsl_warn("User code denied connection\n");
- goto bail_nuke_ah;
- }
-
- /*
- * Perform the handshake according to the protocol version the
- * client announced
- */
-
- switch (wsi->ietf_spec_revision) {
- case 13:
- lwsl_parser("lws_parse calling handshake_04\n");
- if (handshake_0405(context, wsi)) {
- lwsl_info("hs0405 has failed the connection\n");
- goto bail_nuke_ah;
- }
- break;
-
- default:
- lwsl_info("Unknown client spec version %d\n",
- wsi->ietf_spec_revision);
- goto bail_nuke_ah;
- }
-
- lws_same_vh_protocol_insert(wsi, n);
-
- /* we are upgrading to ws, so http/1.1 and keepalive +
- * pipelined header considerations about keeping the ah around
- * no longer apply. However it's common for the first ws
- * protocol data to have been coalesced with the browser
- * upgrade request and to already be in the ah rx buffer.
- */
-
- lwsl_info("%s: %p: inheriting ws ah (rxpos:%d, rxlen:%d)\n",
- __func__, wsi, wsi->u.hdr.ah->rxpos,
- wsi->u.hdr.ah->rxlen);
- lws_pt_lock(pt);
- hdr = wsi->u.hdr;
-
- lws_union_transition(wsi, LWSCM_WS_SERVING);
- /*
- * first service is WS mode will notice this, use the RX and
- * then detach the ah (caution: we are not in u.hdr union
- * mode any more then... ah_temp member is at start the same
- * though)
- *
- * Because rxpos/rxlen shows something in the ah, we will get
- * service guaranteed next time around the event loop
- *
- * All union members begin with hdr, so we can use it even
- * though we transitioned to ws union mode (the ah detach
- * code uses it anyway).
- */
- wsi->u.hdr = hdr;
- lws_pt_unlock(pt);
-
- lws_server_init_wsi_for_ws(wsi);
- lwsl_parser("accepted v%02d connection\n",
- wsi->ietf_spec_revision);
-
- /* !!! drop ah unreservedly after ESTABLISHED */
- if (!wsi->more_rx_waiting) {
- lws_header_table_force_to_detachable_state(wsi);
- lws_header_table_detach(wsi, 1);
- }
-
- return 0;
- } /* while all chars are handled */
-
- return 0;
-
-bail_nuke_ah:
- /* drop the header info */
- /* we're closing, losing some rx is OK */
- lws_header_table_force_to_detachable_state(wsi);
- lws_header_table_detach(wsi, 1);
-
- return 1;
-}
-
-
-static int
-lws_get_idlest_tsi(struct lws_context *context)
-{
- unsigned int lowest = ~0;
- int n = 0, hit = -1;
-
- for (; n < context->count_threads; n++) {
- if ((unsigned int)context->pt[n].fds_count !=
- context->fd_limit_per_thread - 1 &&
- (unsigned int)context->pt[n].fds_count < lowest) {
- lowest = context->pt[n].fds_count;
- hit = n;
- }
- }
-
- return hit;
-}
-
-struct lws *
-lws_create_new_server_wsi(struct lws_vhost *vhost)
-{
- struct lws *new_wsi;
- int n = lws_get_idlest_tsi(vhost->context);
-
- if (n < 0) {
- lwsl_err("no space for new conn\n");
- return NULL;
- }
-
- new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi");
- if (new_wsi == NULL) {
- lwsl_err("Out of memory for new connection\n");
- return NULL;
- }
-
- new_wsi->tsi = n;
- lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi,
- vhost->name, new_wsi->tsi);
-
- new_wsi->vhost = vhost;
- new_wsi->context = vhost->context;
- new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
- new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
-
- /* initialize the instance struct */
-
- new_wsi->state = LWSS_HTTP;
- new_wsi->mode = LWSCM_HTTP_SERVING;
- new_wsi->hdr_parsing_completed = 0;
-
-#ifdef LWS_OPENSSL_SUPPORT
- new_wsi->use_ssl = LWS_SSL_ENABLED(vhost);
-#endif
-
- /*
- * these can only be set once the protocol is known
- * we set an un-established connection's protocol pointer
- * to the start of the supported list, so it can look
- * for matching ones during the handshake
- */
- new_wsi->protocol = vhost->protocols;
- new_wsi->user_space = NULL;
- new_wsi->ietf_spec_revision = 0;
- new_wsi->desc.sockfd = LWS_SOCK_INVALID;
- new_wsi->position_in_fds_table = -1;
-
- vhost->context->count_wsi_allocated++;
-
- /*
- * outermost create notification for wsi
- * no user_space because no protocol selection
- */
- vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
- NULL, NULL, 0);
-
- return new_wsi;
-}
-
-LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
-lws_http_transaction_completed(struct lws *wsi)
-{
- int n = NO_PENDING_TIMEOUT;
-
- lwsl_info("%s: wsi %p\n", __func__, wsi);
-
- lws_access_log(wsi);
-
- if (!wsi->hdr_parsing_completed) {
- lwsl_notice("%s: ignoring, ah parsing incomplete\n", __func__);
- return 0;
- }
-
- lwsl_debug("%s: wsi %p\n", __func__, wsi);
- /* if we can't go back to accept new headers, drop the connection */
- if (wsi->http2_substream)
- return 0;
-
- if (wsi->seen_zero_length_recv)
- return 1;
-
- if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
- lwsl_info("%s: %p: close connection\n", __func__, wsi);
- return 1;
- }
-
- if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0]))
- return 1;
-
- /* otherwise set ourselves up ready to go again */
- wsi->state = LWSS_HTTP;
- wsi->mode = LWSCM_HTTP_SERVING;
- wsi->u.http.tx_content_length = 0;
- wsi->u.http.tx_content_remain = 0;
- wsi->hdr_parsing_completed = 0;
-#ifdef LWS_WITH_ACCESS_LOG
- wsi->access_log.sent = 0;
-#endif
-
- if (wsi->vhost->keepalive_timeout)
- n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;
- lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout);
-
- /*
- * We already know we are on http1.1 / keepalive and the next thing
- * coming will be another header set.
- *
- * If there is no pending rx and we still have the ah, drop it and
- * reacquire a new ah when the new headers start to arrive. (Otherwise
- * we needlessly hog an ah indefinitely.)
- *
- * However if there is pending rx and we know from the keepalive state
- * that is already at least the start of another header set, simply
- * reset the existing header table and keep it.
- */
- if (wsi->u.hdr.ah) {
- lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
- wsi->more_rx_waiting);
-
- if (!wsi->more_rx_waiting) {
- lws_header_table_force_to_detachable_state(wsi);
- lws_header_table_detach(wsi, 1);
-#ifdef LWS_OPENSSL_SUPPORT
- /*
- * additionally... if we are hogging an SSL instance
- * with no pending pipelined headers (or ah now), and
- * SSL is scarce, drop this connection without waiting
- */
-
- if (wsi->vhost->use_ssl &&
- wsi->context->simultaneous_ssl_restriction &&
- wsi->context->simultaneous_ssl ==
- wsi->context->simultaneous_ssl_restriction) {
- lwsl_info("%s: simultaneous_ssl_restriction\n",
- __func__);
- return 1;
- }
-#endif
- } else {
- lws_header_table_reset(wsi, 1);
- /*
- * If we kept the ah, we should restrict the amount
- * of time we are willing to keep it. Otherwise it
- * will be bound the whole time the connection remains
- * open.
- */
- lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
- wsi->vhost->keepalive_timeout);
- }
- }
-
- /* If we're (re)starting on headers, need other implied init */
- wsi->u.hdr.ues = URIES_IDLE;
-
- lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi);
-
- return 0;
-}
-
-/* if not a socket, it's a raw, non-ssl file descriptor */
-
-LWS_VISIBLE struct lws *
-lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
- lws_sock_file_fd_type fd, const char *vh_prot_name,
- struct lws *parent)
-{
- struct lws_context *context = vh->context;
- struct lws *new_wsi;
- struct lws_context_per_thread *pt;
- int n, ssl = 0;
-
-#if defined(LWS_WITH_PEER_LIMITS)
- struct lws_peer *peer = NULL;
-
- if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) {
- peer = lws_get_or_create_peer(vh, fd.sockfd);
-
- if (!peer) {
- lwsl_err("OOM creating peer\n");
- return NULL;
- }
- if (context->ip_limit_wsi &&
- peer->count_wsi >= context->ip_limit_wsi) {
- lwsl_notice("Peer reached wsi limit %d\n",
- context->ip_limit_wsi);
- lws_stats_atomic_bump(context, &context->pt[0],
- LWSSTATS_C_PEER_LIMIT_WSI_DENIED, 1);
- return NULL;
- }
- }
-#endif
-
- new_wsi = lws_create_new_server_wsi(vh);
- if (!new_wsi) {
- if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO))
- compatible_close(fd.sockfd);
- return NULL;
- }
-#if defined(LWS_WITH_PEER_LIMITS)
- if (peer)
- lws_peer_add_wsi(context, peer, new_wsi);
-#endif
- pt = &context->pt[(int)new_wsi->tsi];
- lws_stats_atomic_bump(context, pt, LWSSTATS_C_CONNECTIONS, 1);
-
- if (parent) {
- new_wsi->parent = parent;
- new_wsi->sibling_list = parent->child_list;
- parent->child_list = new_wsi;
-
- if (type & LWS_ADOPT_WS_PARENTIO)
- new_wsi->parent_carries_io = 1;
- }
-
- new_wsi->desc = fd;
-
- if (vh_prot_name) {
- new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost,
- vh_prot_name);
- if (!new_wsi->protocol) {
- lwsl_err("Protocol %s not enabled on vhost %s\n",
- vh_prot_name, new_wsi->vhost->name);
- goto bail;
- }
- if (lws_ensure_user_space(new_wsi)) {
- lwsl_notice("OOM trying to get user_space\n");
- goto bail;
- }
- if (type & LWS_ADOPT_WS_PARENTIO) {
- new_wsi->desc.sockfd = LWS_SOCK_INVALID;
- lwsl_debug("binding to %s\n", new_wsi->protocol->name);
- lws_bind_protocol(new_wsi, new_wsi->protocol);
- lws_union_transition(new_wsi, LWSCM_WS_SERVING);
- lws_server_init_wsi_for_ws(new_wsi);
-
- return new_wsi;
- }
- } else
- if (type & LWS_ADOPT_HTTP) /* he will transition later */
- new_wsi->protocol =
- &vh->protocols[vh->default_protocol_index];
- else { /* this is the only time he will transition */
- lws_bind_protocol(new_wsi,
- &vh->protocols[vh->raw_protocol_index]);
- lws_union_transition(new_wsi, LWSCM_RAW);
- }
-
- if (type & LWS_ADOPT_SOCKET) { /* socket desc */
- lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi,
- (int)(lws_intptr_t)fd.sockfd);
-
- if (type & LWS_ADOPT_HTTP)
- /* the transport is accepted...
- * give him time to negotiate */
- lws_set_timeout(new_wsi,
- PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
- context->timeout_secs);
-
-#if LWS_POSIX == 0
-#if defined(LWS_WITH_ESP8266)
- esp8266_tcp_stream_accept(accept_fd, new_wsi);
-#endif
-#endif
- } else /* file desc */
- lwsl_debug("%s: new wsi %p, filefd %d\n", __func__, new_wsi,
- (int)(lws_intptr_t)fd.filefd);
-
- /*
- * A new connection was accepted. Give the user a chance to
- * set properties of the newly created wsi. There's no protocol
- * selected yet so we issue this to the vhosts's default protocol,
- * itself by default protocols[0]
- */
- n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
- if (!(type & LWS_ADOPT_HTTP)) {
- if (!(type & LWS_ADOPT_SOCKET))
- n = LWS_CALLBACK_RAW_ADOPT_FILE;
- else
- n = LWS_CALLBACK_RAW_ADOPT;
- }
-
- if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_ALLOW_SSL) ||
- !(type & LWS_ADOPT_SOCKET)) {
- /* non-SSL */
- if (!(type & LWS_ADOPT_HTTP)) {
- if (!(type & LWS_ADOPT_SOCKET))
- new_wsi->mode = LWSCM_RAW_FILEDESC;
- else
- new_wsi->mode = LWSCM_RAW;
- }
- } else {
- /* SSL */
- if (!(type & LWS_ADOPT_HTTP))
- new_wsi->mode = LWSCM_SSL_INIT_RAW;
- else
- new_wsi->mode = LWSCM_SSL_INIT;
-
- ssl = 1;
- }
-
- lws_libev_accept(new_wsi, new_wsi->desc);
- lws_libuv_accept(new_wsi, new_wsi->desc);
- lws_libevent_accept(new_wsi, new_wsi->desc);
-
- if (!ssl) {
- if (insert_wsi_socket_into_fds(context, new_wsi)) {
- lwsl_err("%s: fail inserting socket\n", __func__);
- goto fail;
- }
- } else
- if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) {
- lwsl_info("%s: fail ssl negotiation\n", __func__);
- goto fail;
- }
-
- /*
- * by deferring callback to this point, after insertion to fds,
- * lws_callback_on_writable() can work from the callback
- */
- if ((new_wsi->protocol->callback)(
- new_wsi, n, new_wsi->user_space, NULL, 0))
- goto fail;
-
- if (type & LWS_ADOPT_HTTP) {
- if (!lws_header_table_attach(new_wsi, 0))
- lwsl_debug("Attached ah immediately\n");
- else
- lwsl_info("%s: waiting for ah\n", __func__);
- }
-
- return new_wsi;
-
-fail:
- if (type & LWS_ADOPT_SOCKET)
- lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
- return NULL;
-
-bail:
- lwsl_notice("%s: exiting on bail\n", __func__);
- if (parent)
- parent->child_list = new_wsi->sibling_list;
- if (new_wsi->user_space)
- lws_free(new_wsi->user_space);
- lws_free(new_wsi);
- compatible_close(fd.sockfd);
-
- return NULL;
-}
-
-LWS_VISIBLE struct lws *
-lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd)
-{
- lws_sock_file_fd_type fd;
-
- fd.sockfd = accept_fd;
- return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET |
- LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL);
-}
-
-LWS_VISIBLE struct lws *
-lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
-{
- return lws_adopt_socket_vhost(context->vhost_list, accept_fd);
-}
-
-/* Common read-buffer adoption for lws_adopt_*_readbuf */
-static struct lws*
-adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
-{
- struct lws_context_per_thread *pt;
- struct allocated_headers *ah;
- struct lws_pollfd *pfd;
-
- if (!wsi)
- return NULL;
-
- if (!readbuf || len == 0)
- return wsi;
-
- if (len > sizeof(ah->rx)) {
- lwsl_err("%s: rx in too big\n", __func__);
- goto bail;
- }
-
- /*
- * we can't process the initial read data until we can attach an ah.
- *
- * if one is available, get it and place the data in his ah rxbuf...
- * wsi with ah that have pending rxbuf get auto-POLLIN service.
- *
- * no autoservice because we didn't get a chance to attach the
- * readbuf data to wsi or ah yet, and we will do it next if we get
- * the ah.
- */
- if (wsi->u.hdr.ah || !lws_header_table_attach(wsi, 0)) {
- ah = wsi->u.hdr.ah;
- memcpy(ah->rx, readbuf, len);
- ah->rxpos = 0;
- ah->rxlen = (int16_t)len;
-
- lwsl_notice("%s: calling service on readbuf ah\n", __func__);
- pt = &wsi->context->pt[(int)wsi->tsi];
-
- /* unlike a normal connect, we have the headers already
- * (or the first part of them anyway).
- * libuv won't come back and service us without a network
- * event, so we need to do the header service right here.
- */
- pfd = &pt->fds[wsi->position_in_fds_table];
- pfd->revents |= LWS_POLLIN;
- lwsl_err("%s: calling service\n", __func__);
- if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi))
- /* service closed us */
- return NULL;
-
- return wsi;
- }
- lwsl_err("%s: deferring handling ah\n", __func__);
- /*
- * hum if no ah came, we are on the wait list and must defer
- * dealing with this until the ah arrives.
- *
- * later successful lws_header_table_attach() will apply the
- * below to the rx buffer (via lws_header_table_reset()).
- */
- wsi->u.hdr.preamble_rx = lws_malloc(len, "preamble_rx");
- if (!wsi->u.hdr.preamble_rx) {
- lwsl_err("OOM\n");
- goto bail;
- }
- memcpy(wsi->u.hdr.preamble_rx, readbuf, len);
- wsi->u.hdr.preamble_rx_len = len;
-
- return wsi;
-
-bail:
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
- return NULL;
-}
-
-LWS_VISIBLE struct lws *
-lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
- const char *readbuf, size_t len)
-{
- return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd),
- readbuf, len);
-}
-
-LWS_VISIBLE struct lws *
-lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
- lws_sockfd_type accept_fd,
- const char *readbuf, size_t len)
-{
- return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd),
- readbuf, len);
-}
-
-LWS_VISIBLE int
-lws_server_socket_service(struct lws_context *context, struct lws *wsi,
- struct lws_pollfd *pollfd)
-{
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
- struct allocated_headers *ah;
- lws_sock_file_fd_type fd;
- int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL;
-#if LWS_POSIX
- struct sockaddr_storage cli_addr;
- socklen_t clilen;
-#endif
- int n, len;
-
- switch (wsi->mode) {
-
- case LWSCM_HTTP_SERVING:
- case LWSCM_HTTP_SERVING_ACCEPTED:
- case LWSCM_HTTP2_SERVING:
- case LWSCM_RAW:
-
- /* handle http headers coming in */
-
- /* pending truncated sends have uber priority */
-
- if (wsi->trunc_len) {
- if (!(pollfd->revents & LWS_POLLOUT))
- break;
-
- if (lws_issue_raw(wsi, wsi->trunc_alloc +
- wsi->trunc_offset,
- wsi->trunc_len) < 0)
- goto fail;
- /*
- * we can't afford to allow input processing to send
- * something new, so spin around he event loop until
- * he doesn't have any partials
- */
- break;
- }
-
- /* any incoming data ready? */
-
- if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
- goto try_pollout;
-
- /*
- * If we previously just did POLLIN when IN and OUT were
- * signalled (because POLLIN processing may have used up
- * the POLLOUT), don't let that happen twice in a row...
- * next time we see the situation favour POLLOUT
- */
-#if !defined(LWS_WITH_ESP8266)
- if (wsi->favoured_pollin &&
- (pollfd->revents & pollfd->events & LWS_POLLOUT)) {
- lwsl_notice("favouring pollout\n");
- wsi->favoured_pollin = 0;
- goto try_pollout;
- }
-#endif
-
- /* these states imply we MUST have an ah attached */
-
- if (wsi->mode != LWSCM_RAW && (wsi->state == LWSS_HTTP ||
- wsi->state == LWSS_HTTP_ISSUING_FILE ||
- wsi->state == LWSS_HTTP_HEADERS)) {
- if (!wsi->u.hdr.ah) {
- /* no autoservice beacuse we will do it next */
- if (lws_header_table_attach(wsi, 0)) {
- lwsl_info("wsi %p: ah get fail\n", wsi);
- goto try_pollout;
- }
- }
- ah = wsi->u.hdr.ah;
-
- /* if nothing in ah rx buffer, get some fresh rx */
- if (ah->rxpos == ah->rxlen) {
- ah->rxlen = lws_ssl_capable_read(wsi, ah->rx,
- sizeof(ah->rx));
- ah->rxpos = 0;
- switch (ah->rxlen) {
- case 0:
- lwsl_info("%s: read 0 len a\n", __func__);
- wsi->seen_zero_length_recv = 1;
- lws_change_pollfd(wsi, LWS_POLLIN, 0);
- goto try_pollout;
- /* fallthru */
- case LWS_SSL_CAPABLE_ERROR:
- goto fail;
- case LWS_SSL_CAPABLE_MORE_SERVICE:
- ah->rxlen = ah->rxpos = 0;
- goto try_pollout;
- }
-
- /*
- * make sure ah does not get detached if we
- * have live data in the rx
- */
- if (ah->rxlen)
- wsi->more_rx_waiting = 1;
- }
-
- if (!(ah->rxpos != ah->rxlen && ah->rxlen)) {
- lwsl_err("%s: assert: rxpos %d, rxlen %d\n",
- __func__, ah->rxpos, ah->rxlen);
-
- assert(0);
- }
-
- /* just ignore incoming if waiting for close */
- if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
- wsi->state != LWSS_HTTP_ISSUING_FILE) {
- /*
- * otherwise give it to whoever wants it
- * according to the connection state
- */
-
- n = lws_read(wsi, ah->rx + ah->rxpos,
- ah->rxlen - ah->rxpos);
- if (n < 0) /* we closed wsi */
- return 1;
-
- if (!wsi->u.hdr.ah)
- break;
- if ( wsi->u.hdr.ah->rxlen)
- wsi->u.hdr.ah->rxpos += n;
-
- lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n",
- __func__, wsi, wsi->u.hdr.ah->rxpos,
- wsi->u.hdr.ah->rxlen);
-
- if (lws_header_table_is_in_detachable_state(wsi) &&
- (wsi->mode != LWSCM_HTTP_SERVING &&
- wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED &&
- wsi->mode != LWSCM_HTTP2_SERVING))
- lws_header_table_detach(wsi, 1);
-
- break;
- }
-
- goto try_pollout;
- }
-
- len = lws_ssl_capable_read(wsi, pt->serv_buf,
- context->pt_serv_buf_size);
- lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
- switch (len) {
- case 0:
- lwsl_info("%s: read 0 len b\n", __func__);
-
- /* fallthru */
- case LWS_SSL_CAPABLE_ERROR:
- goto fail;
- case LWS_SSL_CAPABLE_MORE_SERVICE:
- goto try_pollout;
- }
-
- if (len < 0) /* coverity */
- goto fail;
-
- if (wsi->mode == LWSCM_RAW) {
- n = user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_RAW_RX,
- wsi->user_space, pt->serv_buf, len);
- if (n < 0) {
- lwsl_info("LWS_CALLBACK_RAW_RX_fail\n");
- goto fail;
- }
- goto try_pollout;
- }
-
- /* just ignore incoming if waiting for close */
- if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
- wsi->state != LWSS_HTTP_ISSUING_FILE) {
- /*
- * this may want to send
- * (via HTTP callback for example)
- */
- n = lws_read(wsi, pt->serv_buf, len);
- if (n < 0) /* we closed wsi */
- return 1;
- /*
- * he may have used up the
- * writability above, if we will defer POLLOUT
- * processing in favour of POLLIN, note it
- */
- if (pollfd->revents & LWS_POLLOUT)
- wsi->favoured_pollin = 1;
- break;
- }
- /*
- * he may have used up the
- * writability above, if we will defer POLLOUT
- * processing in favour of POLLIN, note it
- */
- if (pollfd->revents & LWS_POLLOUT)
- wsi->favoured_pollin = 1;
-
-try_pollout:
-
- /* this handles POLLOUT for http serving fragments */
-
- if (!(pollfd->revents & LWS_POLLOUT))
- break;
-
- /* one shot */
- if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
- lwsl_notice("%s a\n", __func__);
- goto fail;
- }
-
- if (wsi->mode == LWSCM_RAW) {
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_C_WRITEABLE_CB, 1);
-#if defined(LWS_WITH_STATS)
- if (wsi->active_writable_req_us) {
- uint64_t ul = time_in_microseconds() -
- wsi->active_writable_req_us;
-
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_MS_WRITABLE_DELAY, ul);
- lws_stats_atomic_max(wsi->context, pt,
- LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
- wsi->active_writable_req_us = 0;
- }
-#endif
- n = user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_RAW_WRITEABLE,
- wsi->user_space, NULL, 0);
- if (n < 0) {
- lwsl_info("writeable_fail\n");
- goto fail;
- }
- break;
- }
-
- if (!wsi->hdr_parsing_completed)
- break;
-
- if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
-
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_C_WRITEABLE_CB, 1);
-#if defined(LWS_WITH_STATS)
- if (wsi->active_writable_req_us) {
- uint64_t ul = time_in_microseconds() -
- wsi->active_writable_req_us;
-
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_MS_WRITABLE_DELAY, ul);
- lws_stats_atomic_max(wsi->context, pt,
- LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
- wsi->active_writable_req_us = 0;
- }
-#endif
-
- n = user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_HTTP_WRITEABLE,
- wsi->user_space, NULL, 0);
- if (n < 0) {
- lwsl_info("writeable_fail\n");
- goto fail;
- }
- break;
- }
-
- /* >0 == completion, <0 == error
- *
- * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
- * it's done. That's the case even if we just completed the
- * send, so wait for that.
- */
- n = lws_serve_http_file_fragment(wsi);
- if (n < 0)
- goto fail;
-
- break;
-
- case LWSCM_SERVER_LISTENER:
-
-#if LWS_POSIX
- /* pollin means a client has connected to us then */
-
- do {
- if (!(pollfd->revents & LWS_POLLIN) ||
- !(pollfd->events & LWS_POLLIN))
- break;
-
-#ifdef LWS_OPENSSL_SUPPORT
- /*
- * can we really accept it, with regards to SSL limit?
- * another vhost may also have had POLLIN on his listener this
- * round and used it up already
- */
-
- if (wsi->vhost->use_ssl &&
- context->simultaneous_ssl_restriction &&
- context->simultaneous_ssl ==
- context->simultaneous_ssl_restriction)
- /* no... ignore it, he won't come again until we are
- * below the simultaneous_ssl_restriction limit and
- * POLLIN is enabled on him again
- */
- break;
-#endif
- /* listen socket got an unencrypted connection... */
-
- clilen = sizeof(cli_addr);
- lws_latency_pre(context, wsi);
-
- /*
- * We cannot identify the peer who is in the listen
- * socket connect queue before we accept it; even if
- * we could, not accepting it due to PEER_LIMITS would
- * block the connect queue for other legit peers.
- */
- accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr,
- &clilen);
- lws_latency(context, wsi, "listener accept", accept_fd,
- accept_fd >= 0);
- if (accept_fd < 0) {
- if (LWS_ERRNO == LWS_EAGAIN ||
- LWS_ERRNO == LWS_EWOULDBLOCK) {
- break;
- }
- lwsl_err("ERROR on accept: %s\n", strerror(LWS_ERRNO));
- break;
- }
-
- lws_plat_set_socket_options(wsi->vhost, accept_fd);
-
-#if defined(LWS_WITH_IPV6)
- lwsl_debug("accepted new conn port %u on fd=%d\n",
- ((cli_addr.ss_family == AF_INET6) ?
- ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) :
- ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)),
- accept_fd);
-#else
- lwsl_debug("accepted new conn port %u on fd=%d\n",
- ntohs(((struct sockaddr_in *) &cli_addr)->sin_port),
- accept_fd);
-#endif
-
-#else
- /* not very beautiful... */
- accept_fd = (lws_sockfd_type)pollfd;
-#endif
- /*
- * look at who we connected to and give user code a chance
- * to reject based on client IP. There's no protocol selected
- * yet so we issue this to protocols[0]
- */
- if ((wsi->vhost->protocols[0].callback)(wsi,
- LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
- NULL, (void *)(lws_intptr_t)accept_fd, 0)) {
- lwsl_debug("Callback denied network connection\n");
- compatible_close(accept_fd);
- break;
- }
-
- if (!(wsi->vhost->options & LWS_SERVER_OPTION_ONLY_RAW))
- opts |= LWS_ADOPT_HTTP;
- else
- opts = LWS_ADOPT_SOCKET;
-
- fd.sockfd = accept_fd;
- if (!lws_adopt_descriptor_vhost(wsi->vhost, opts, fd,
- NULL, NULL))
- /* already closed cleanly as necessary */
- return 1;
-
-#if LWS_POSIX
- } while (pt->fds_count < context->fd_limit_per_thread - 1 &&
- lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0);
-#endif
- return 0;
-
- default:
- break;
- }
-
- if (!lws_server_socket_service_ssl(wsi, accept_fd))
- return 0;
-
-fail:
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
- return 1;
-}
-
-LWS_VISIBLE int
-lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
- const char *other_headers, int other_headers_len)
-{
- static const char * const intermediates[] = { "private", "public" };
- struct lws_context *context = lws_get_context(wsi);
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-#if defined(LWS_WITH_RANGES)
- struct lws_range_parsing *rp = &wsi->u.http.range;
-#endif
- char cache_control[50], *cc = "no-store";
- unsigned char *response = pt->serv_buf + LWS_PRE;
- unsigned char *p = response;
- unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
- lws_filepos_t computed_total_content_length;
- int ret = 0, cclen = 8, n = HTTP_STATUS_OK;
- lws_fop_flags_t fflags = LWS_O_RDONLY;
-#if defined(LWS_WITH_RANGES)
- int ranges;
-#endif
- const struct lws_plat_file_ops *fops;
- const char *vpath;
-
- /*
- * We either call the platform fops .open with first arg platform fops,
- * or we call fops_zip .open with first arg platform fops, and fops_zip
- * open will decide whether to switch to fops_zip or stay with fops_def.
- *
- * If wsi->u.http.fop_fd is already set, the caller already opened it
- */
- if (!wsi->u.http.fop_fd) {
- fops = lws_vfs_select_fops(wsi->context->fops, file, &vpath);
- fflags |= lws_vfs_prepare_flags(wsi);
- wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
- file, vpath, &fflags);
- if (!wsi->u.http.fop_fd) {
- lwsl_err("Unable to open '%s'\n", file);
-
- return -1;
- }
- }
- wsi->u.http.filelen = lws_vfs_get_length(wsi->u.http.fop_fd);
- computed_total_content_length = wsi->u.http.filelen;
-
-#if defined(LWS_WITH_RANGES)
- ranges = lws_ranges_init(wsi, rp, wsi->u.http.filelen);
-
- lwsl_debug("Range count %d\n", ranges);
- /*
- * no ranges -> 200;
- * 1 range -> 206 + Content-Type: normal; Content-Range;
- * more -> 206 + Content-Type: multipart/byteranges
- * Repeat the true Content-Type in each multipart header
- * along with Content-Range
- */
- if (ranges < 0) {
- /* it means he expressed a range in Range:, but it was illegal */
- lws_return_http_status(wsi, HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE,
- NULL);
- if (lws_http_transaction_completed(wsi))
- return -1; /* <0 means just hang up */
-
- lws_vfs_file_close(&wsi->u.http.fop_fd);
-
- return 0; /* == 0 means we dealt with the transaction complete */
- }
- if (ranges)
- n = HTTP_STATUS_PARTIAL_CONTENT;
-#endif
-
- if (lws_add_http_header_status(wsi, n, &p, end))
- return -1;
-
- if ((wsi->u.http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
- LWS_FOP_FLAG_COMPR_IS_GZIP)) ==
- (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {
- if (lws_add_http_header_by_token(wsi,
- WSI_TOKEN_HTTP_CONTENT_ENCODING,
- (unsigned char *)"gzip", 4, &p, end))
- return -1;
- lwsl_info("file is being provided in gzip\n");
- }
-
- if (
-#if defined(LWS_WITH_RANGES)
- ranges < 2 &&
-#endif
- content_type && content_type[0])
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
- (unsigned char *)content_type,
- strlen(content_type), &p, end))
- return -1;
-
-#if defined(LWS_WITH_RANGES)
- if (ranges >= 2) { /* multipart byteranges */
- strncpy(wsi->u.http.multipart_content_type, content_type,
- sizeof(wsi->u.http.multipart_content_type) - 1);
- wsi->u.http.multipart_content_type[
- sizeof(wsi->u.http.multipart_content_type) - 1] = '\0';
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
- (unsigned char *)"multipart/byteranges; boundary=_lws",
- 20, &p, end))
- return -1;
-
- /*
- * our overall content length has to include
- *
- * - (n + 1) x "_lws\r\n"
- * - n x Content-Type: xxx/xxx\r\n
- * - n x Content-Range: bytes xxx-yyy/zzz\r\n
- * - n x /r/n
- * - the actual payloads (aggregated in rp->agg)
- *
- * Precompute it for the main response header
- */
-
- computed_total_content_length = (lws_filepos_t)rp->agg +
- 6 /* final _lws\r\n */;
-
- lws_ranges_reset(rp);
- while (lws_ranges_next(rp)) {
- n = lws_snprintf(cache_control, sizeof(cache_control),
- "bytes %llu-%llu/%llu",
- rp->start, rp->end, rp->extent);
-
- computed_total_content_length +=
- 6 /* header _lws\r\n */ +
- /* Content-Type: xxx/xxx\r\n */
- 14 + strlen(content_type) + 2 +
- /* Content-Range: xxxx\r\n */
- 15 + n + 2 +
- 2; /* /r/n */
- }
-
- lws_ranges_reset(rp);
- lws_ranges_next(rp);
- }
-
- if (ranges == 1) {
- computed_total_content_length = (lws_filepos_t)rp->agg;
- n = lws_snprintf(cache_control, sizeof(cache_control),
- "bytes %llu-%llu/%llu",
- rp->start, rp->end, rp->extent);
-
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_RANGE,
- (unsigned char *)cache_control,
- n, &p, end))
- return -1;
- }
-
- wsi->u.http.range.inside = 0;
-
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES,
- (unsigned char *)"bytes", 5, &p, end))
- return -1;
-#endif
-
- if (!wsi->sending_chunked) {
- if (lws_add_http_header_content_length(wsi,
- computed_total_content_length,
- &p, end))
- return -1;
- } else {
- if (lws_add_http_header_by_token(wsi,
- WSI_TOKEN_HTTP_TRANSFER_ENCODING,
- (unsigned char *)"chunked",
- 7, &p, end))
- return -1;
- }
-
- if (wsi->cache_secs && wsi->cache_reuse) {
- if (wsi->cache_revalidate) {
- cc = cache_control;
- cclen = sprintf(cache_control, "%s max-age: %u",
- intermediates[wsi->cache_intermediaries],
- wsi->cache_secs);
- } else {
- cc = "no-cache";
- cclen = 8;
- }
- }
-
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CACHE_CONTROL,
- (unsigned char *)cc, cclen, &p, end))
- return -1;
-
- if (wsi->u.http.connection_type == HTTP_CONNECTION_KEEP_ALIVE)
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
- (unsigned char *)"keep-alive", 10, &p, end))
- return -1;
-
- if (other_headers) {
- if ((end - p) < other_headers_len)
- return -1;
- memcpy(p, other_headers, other_headers_len);
- p += other_headers_len;
- }
-
- if (lws_finalize_http_header(wsi, &p, end))
- return -1;
-
- ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS);
- if (ret != (p - response)) {
- lwsl_err("_write returned %d from %ld\n", ret,
- (long)(p - response));
- return -1;
- }
-
- wsi->u.http.filepos = 0;
- wsi->state = LWSS_HTTP_ISSUING_FILE;
-
- lws_callback_on_writable(wsi);
-
- return 0;
-}
-
-int
-lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
-{
- int m;
-
- lwsl_parser("%s: received %d byte packet\n", __func__, (int)len);
-#if 0
- lwsl_hexdump(*buf, len);
-#endif
-
- /* let the rx protocol state machine have as much as it needs */
-
- while (len) {
- /*
- * we were accepting input but now we stopped doing so
- */
- if (wsi->rxflow_bitmap) {
- lws_rxflow_cache(wsi, *buf, 0, len);
- lwsl_parser("%s: cached %ld\n", __func__, (long)len);
- return 1;
- }
-
- if (wsi->u.ws.rx_draining_ext) {
- m = lws_rx_sm(wsi, 0);
- if (m < 0)
- return -1;
- continue;
- }
-
- /* account for what we're using in rxflow buffer */
- if (wsi->rxflow_buffer) {
- wsi->rxflow_pos++;
- if (wsi->rxflow_pos > wsi->rxflow_len) {
- lwsl_err("bumped rxflow buffer too far (%d / %d)", wsi->rxflow_pos, wsi->rxflow_len);
- assert(0);
- }
- }
-
- /* consume payload bytes efficiently */
- if (wsi->lws_rx_parse_state ==
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) {
- m = lws_payload_until_length_exhausted(wsi, buf, &len);
- if (wsi->rxflow_buffer)
- wsi->rxflow_pos += m;
- }
-
- if (wsi->rxflow_buffer && wsi->rxflow_pos == wsi->rxflow_len) {
- lwsl_debug("%s: %p flow buf: drained\n", __func__, wsi);
- lws_free_set_NULL(wsi->rxflow_buffer);
- /* having drained the rxflow buffer, can rearm POLLIN */
-#ifdef LWS_NO_SERVER
- m =
-#endif
- _lws_rx_flow_control(wsi);
- /* m ignored, needed for NO_SERVER case */
- }
-
- /* process the byte */
- m = lws_rx_sm(wsi, *(*buf)++);
- if (m < 0)
- return -1;
- len--;
- }
-
- lwsl_parser("%s: exit with %d unused\n", __func__, (int)len);
-
- return 0;
-}
-
-LWS_VISIBLE void
-lws_server_get_canonical_hostname(struct lws_context *context,
- struct lws_context_creation_info *info)
-{
- if (lws_check_opt(info->options,
- LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME))
- return;
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
- /* find canonical hostname */
- gethostname((char *)context->canonical_hostname,
- sizeof(context->canonical_hostname) - 1);
-
- lwsl_info(" canonical_hostname = %s\n", context->canonical_hostname);
-#else
- (void)context;
-#endif
-}
-
-
-LWS_VISIBLE LWS_EXTERN int
-lws_chunked_html_process(struct lws_process_html_args *args,
- struct lws_process_html_state *s)
-{
- char *sp, buffer[32];
- const char *pc;
- int old_len, n;
-
- /* do replacements */
- sp = args->p;
- old_len = args->len;
- args->len = 0;
- s->start = sp;
- while (sp < args->p + old_len) {
-
- if (args->len + 7 >= args->max_len) {
- lwsl_err("Used up interpret padding\n");
- return -1;
- }
-
- if ((!s->pos && *sp == '$') || s->pos) {
- int hits = 0, hit = 0;
-
- if (!s->pos)
- s->start = sp;
- s->swallow[s->pos++] = *sp;
- if (s->pos == sizeof(s->swallow) - 1)
- goto skip;
- for (n = 0; n < s->count_vars; n++)
- if (!strncmp(s->swallow, s->vars[n], s->pos)) {
- hits++;
- hit = n;
- }
- if (!hits) {
-skip:
- s->swallow[s->pos] = '\0';
- memcpy(s->start, s->swallow, s->pos);
- args->len++;
- s->pos = 0;
- sp = s->start + 1;
- continue;
- }
- if (hits == 1 && s->pos == strlen(s->vars[hit])) {
- pc = s->replace(s->data, hit);
- if (!pc)
- pc = "NULL";
- n = strlen(pc);
- s->swallow[s->pos] = '\0';
- if (n != s->pos) {
- memmove(s->start + n,
- s->start + s->pos,
- old_len - (sp - args->p));
- old_len += (n - s->pos) + 1;
- }
- memcpy(s->start, pc, n);
- args->len++;
- sp = s->start + 1;
-
- s->pos = 0;
- }
- sp++;
- continue;
- }
-
- args->len++;
- sp++;
- }
-
- /* no space left for final chunk trailer */
- if (args->final && args->len + 7 >= args->max_len)
- return -1;
-
- n = sprintf(buffer, "%X\x0d\x0a", args->len);
-
- args->p -= n;
- memcpy(args->p, buffer, n);
- args->len += n;
-
- if (args->final) {
- sp = args->p + args->len;
- *sp++ = '\x0d';
- *sp++ = '\x0a';
- *sp++ = '0';
- *sp++ = '\x0d';
- *sp++ = '\x0a';
- *sp++ = '\x0d';
- *sp++ = '\x0a';
- args->len += 7;
- } else {
- sp = args->p + args->len;
- *sp++ = '\x0d';
- *sp++ = '\x0a';
- args->len += 2;
- }
-
- return 0;
-}
diff --git a/thirdparty/lws/server/ssl-server.c b/thirdparty/lws/server/ssl-server.c
deleted file mode 100644
index c4362824bf..0000000000
--- a/thirdparty/lws/server/ssl-server.c
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "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);
-
-#if !defined(LWS_WITH_MBEDTLS)
-static int
-OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
-{
- SSL *ssl;
- int n;
- 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
- */
- wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
-
- n = wsi->vhost->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;
-}
-#endif
-
-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)
-{
-#if defined(LWS_HAVE_OPENSSL_ECDH_H) && !defined(LWS_WITH_MBEDTLS)
- 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
-#if !defined(LWS_WITH_MBEDTLS)
- lwsl_notice(" OpenSSL doesn't support ECDH\n");
-#endif
-#endif
- return 0;
-}
-
-#if !defined(LWS_WITH_MBEDTLS) && defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT)
-static int
-lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
-{
- struct lws_context *context = (struct lws_context *)arg;
- struct lws_vhost *vhost, *vh;
- const char *servername;
-
- if (!ssl)
- return SSL_TLSEXT_ERR_NOACK;
-
- /*
- * 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->being_destroyed && ssl && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
- break;
- vh = vh->vhost_next;
- }
-
- if (!vh) {
- assert(vh); /* can't match the incoming vh? */
- return SSL_TLSEXT_ERR_OK;
- }
-
- servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
- if (!servername) {
- /* the client doesn't know what hostname it wants */
- lwsl_info("SNI: Unknown ServerName: %s\n", servername);
-
- return SSL_TLSEXT_ERR_OK;
- }
-
- vhost = lws_select_vhost(context, vh->listen_port, servername);
- if (!vhost) {
- lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
-
- return SSL_TLSEXT_ERR_OK;
- }
-
- lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port);
-
- /* select the ssl ctx from the selected vhost for this conn */
- SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
-
- return SSL_TLSEXT_ERR_OK;
-}
-#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;
- unsigned long error;
- int n;
-
- if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
- vhost->use_ssl = 0;
- return 0;
- }
-
- /*
- * If he is giving a cert filepath, take it as a sign he wants to use
- * it on this vhost. User code can leave the cert filepath NULL and
- * set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in
- * which case he's expected to set up the cert himself at
- * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
- * provides the vhost SSL_CTX * in the user parameter.
- */
- if (info->ssl_cert_filepath)
- info->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX;
-
- if (info->port != CONTEXT_PORT_NO_LISTEN) {
-
- vhost->use_ssl = lws_check_opt(info->options,
- LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX);
-
- 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;
-
- /*
- * 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()
- */
-#if !defined(LWS_WITH_MBEDTLS)
- {
- SSL_METHOD *method;
-
- method = (SSL_METHOD *)SSLv23_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;
- }
- }
-#else
- {
- const SSL_METHOD *method = TLSv1_2_server_method();
-
- vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
- if (!vhost->ssl_ctx) {
- lwsl_err("problem creating ssl context\n");
- return 1;
- }
-
- }
-#endif
-#if !defined(LWS_WITH_MBEDTLS)
-
- /* associate the lws context with the SSL_CTX */
-
- SSL_CTX_set_ex_data(vhost->ssl_ctx,
- openssl_SSL_CTX_private_data_index, (char *)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);
-#endif
-
- /* 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;
-
-#if !defined(LWS_WITH_MBEDTLS)
- 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);
-#endif
- }
-
-#if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT)
- SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
- lws_ssl_server_name_cb);
- SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, context);
-#endif
-
- /*
- * give user code a chance to load certs into the server
- * allowing it to verify incoming client certs
- */
-#if !defined(LWS_WITH_MBEDTLS)
- 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__);
- }
-#endif
- 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);
-
-/* SSL_clear_options introduced in 0.9.8m */
-#if !defined(LWS_WITH_MBEDTLS)
-#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL)
- if (info->ssl_options_clear)
- SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear);
-#endif
-#endif
-
- lwsl_info(" SSL options 0x%lX\n", SSL_CTX_get_options(vhost->ssl_ctx));
-
- if (vhost->use_ssl && info->ssl_cert_filepath) {
- /*
- * The user code can choose to either pass the cert and
- * key filepaths using the info members like this, or it can
- * leave them NULL; force the vhost SSL_CTX init using the info
- * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
- * set up the cert himself using the user callback
- * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
- * happened just above and has the vhost SSL_CTX * in the user
- * parameter.
- */
-#if !defined(LWS_WITH_MBEDTLS)
- /* 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);
-#else
- uint8_t *p;
- lws_filepos_t flen;
- int err;
-
- if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p,
- &flen)) {
- lwsl_err("couldn't find cert file %s\n",
- info->ssl_cert_filepath);
-
- return 1;
- }
- err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p);
- if (!err) {
- lwsl_err("Problem loading cert\n");
- return 1;
- }
-#if !defined(LWS_WITH_ESP32)
- free(p);
- p = NULL;
-#endif
-
- if (info->ssl_private_key_filepath) {
- if (alloc_pem_to_der_file(vhost->context,
- info->ssl_private_key_filepath, &p, &flen)) {
- lwsl_err("couldn't find cert file %s\n",
- info->ssl_cert_filepath);
-
- return 1;
- }
- err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
- if (!err) {
- lwsl_err("Problem loading key\n");
-
- return 1;
- }
- }
-
-#if !defined(LWS_WITH_ESP32)
- free(p);
- p = NULL;
-#endif
-#endif
- if (info->ssl_private_key_filepath != NULL) {
-#if !defined(LWS_WITH_MBEDTLS)
- /* 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;
- }
-#endif
- } 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;
- }
-#if !defined(LWS_WITH_MBEDTLS)
- /* verify private key */
- if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
- lwsl_err("Private SSL key doesn't match cert\n");
- return 1;
- }
-#endif
- }
- if (vhost->use_ssl) {
- 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);
- }
-
- return 0;
-}
-