Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,96 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# 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.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(. ./compression)
list(APPEND SOURCES
roles/http/header.c
roles/http/date.c
roles/http/parsers.c)
if (NOT LWS_WITHOUT_SERVER)
list(APPEND SOURCES
roles/http/server/server.c
roles/http/server/lws-spa.c)
endif()
if (LWS_WITH_CACHE_NSCOOKIEJAR AND LWS_WITH_CLIENT)
list(APPEND SOURCES
roles/http/cookie.c)
endif()
if (LWS_WITH_HTTP_PROXY AND LWS_WITH_HUBBUB)
list(APPEND SOURCES
roles/http/server/rewrite.c)
endif()
if (LWS_WITH_ACCESS_LOG)
list(APPEND SOURCES
roles/http/server/access-log.c)
endif()
if (LWS_WITH_HTTP_STREAM_COMPRESSION)
list(APPEND SOURCES
roles/http/compression/stream.c
roles/http/compression/deflate/deflate.c)
if (LWS_WITH_HTTP_BROTLI)
list(APPEND SOURCES
roles/http/compression/brotli/brotli.c)
list(APPEND LIB_LIST brotlienc brotlidec brotlidec)
endif()
endif()
if (LWS_WITH_LEJP_CONF AND LWS_WITH_NETWORK AND NOT LWS_PLAT_OPTEE)
list(APPEND SOURCES
roles/http/server/lejp-conf.c
)
endif()
if (LWS_WITH_RANGES)
list(APPEND SOURCES
roles/http/server/ranges.c)
endif()
if (LWS_WITH_ZIP_FOPS)
if (LWS_WITH_ZLIB)
list(APPEND SOURCES
roles/http/server/fops-zip.c)
else()
message(FATAL_ERROR "Pre-zipped file support (LWS_WITH_ZIP_FOPS) requires ZLIB (LWS_WITH_ZLIB)")
endif()
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
HTTP compression
----------------
This directory contains generic compression transforms that can be applied to
specifically HTTP content streams, after the header, be it h1 or h2.
The compression transforms expose an "ops" type struct and a compressor name
as used by `content-encoding`... the ops struct definition can be found in
./private-lib-roles-http-compression.h.
Because the compression transform depends on being able to send on its output
before it can process new input, the transform adds a new kind of buflist
`wsi->buflist_comp` that represents pre-compression transform data
("input data" from the perspective of the compression transform) that was
delivered to be processed but couldn't be accepted.
Currently, zlib 'deflate' and brotli 'br' are supported on the server side.

View File

@ -0,0 +1,124 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
static int
lcs_init_compression_brotli(lws_comp_ctx_t *ctx, int decomp)
{
ctx->is_decompression = (unsigned char)!!decomp;
if (!decomp) {
ctx->u.br_en = BrotliEncoderCreateInstance(NULL, NULL, NULL);
if (ctx->u.br_en) {
BrotliEncoderSetParameter(ctx->u.br_en,
BROTLI_PARAM_MODE, BROTLI_MODE_TEXT);
BrotliEncoderSetParameter(ctx->u.br_en,
BROTLI_PARAM_QUALITY, BROTLI_MIN_QUALITY);
}
}
else
ctx->u.br_de = BrotliDecoderCreateInstance(NULL, NULL, NULL);
return !ctx->u.br_de;
}
static int
lcs_process_brotli(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,
void *out, size_t *olen_oused)
{
size_t a_in, a_out, t_out;
const uint8_t *n_in;
uint8_t *n_out;
int n;
n_in = (void *)in;
a_in = *ilen_iused;
a_out = *olen_oused;
n_out = out;
t_out = 0;
if (!ctx->is_decompression) {
if (!a_in && !BrotliEncoderHasMoreOutput(ctx->u.br_en)) {
*olen_oused = 0;
goto bail;
}
n = BROTLI_OPERATION_PROCESS;
if (!ctx->buflist_comp && ctx->final_on_input_side)
n = BROTLI_OPERATION_FINISH;
if (BrotliEncoderCompressStream(ctx->u.br_en, n, &a_in, &n_in,
&a_out, &n_out, &t_out) ==
BROTLI_FALSE) {
lwsl_err("brotli encode failed\n");
return -1;
}
ctx->may_have_more = !a_out;
} else {
n = BrotliDecoderDecompressStream(ctx->u.br_de, &a_in, &n_in,
&a_out, &n_out, &t_out);
switch (n) {
case BROTLI_DECODER_RESULT_ERROR:
lwsl_err("brotli decoder error\n");
return -1;
}
}
*ilen_iused -= a_in;
*olen_oused -= a_out;
bail:
if (!ctx->is_decompression)
return BrotliEncoderIsFinished(ctx->u.br_en);
else
return BrotliDecoderIsFinished(ctx->u.br_de);
}
static void
lcs_destroy_brotli(lws_comp_ctx_t *ctx)
{
if (!ctx)
return;
if (!(*ctx).is_decompression)
BrotliEncoderDestroyInstance((*ctx).u.br_en);
else
BrotliDecoderDestroyInstance((*ctx).u.br_de);
(*ctx).u.generic_ctx_ptr = NULL;
}
struct lws_compression_support lcs_brotli = {
/* .encoding_name */ "br",
/* .init_compression */ lcs_init_compression_brotli,
/* .process */ lcs_process_brotli,
/* .destroy */ lcs_destroy_brotli,
};

View File

@ -0,0 +1,113 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
static int
lcs_init_compression_deflate(lws_comp_ctx_t *ctx, int decomp)
{
int n;
ctx->is_decompression = !!decomp;
ctx->u.deflate = lws_malloc(sizeof(*ctx->u.deflate), __func__);
if (!ctx->u.deflate)
return 2;
memset(ctx->u.deflate, 0, sizeof(*ctx->u.deflate));
if (!decomp &&
(n = deflateInit2(ctx->u.deflate, 1, Z_DEFLATED, -15, 8,
Z_DEFAULT_STRATEGY)) != Z_OK) {
lwsl_err("deflate init failed: %d\n", n);
lws_free_set_NULL(ctx->u.deflate);
return 1;
}
if (decomp &&
inflateInit2(ctx->u.deflate, 16 + 15) != Z_OK) {
lws_free_set_NULL(ctx->u.deflate);
return 1;
}
return 0;
}
static int
lcs_process_deflate(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,
void *out, size_t *olen_oused)
{
size_t olen_oused_in = *olen_oused;
int n;
ctx->u.deflate->next_in = (void *)in;
ctx->u.deflate->avail_in = (unsigned int)*ilen_iused;
ctx->u.deflate->next_out = out;
ctx->u.deflate->avail_out = (unsigned int)*olen_oused;
if (!ctx->is_decompression)
n = deflate(ctx->u.deflate, Z_SYNC_FLUSH);
else
n = inflate(ctx->u.deflate, Z_SYNC_FLUSH);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
lwsl_err("zlib error inflate %d\n", n);
return -1;
}
*ilen_iused -= ctx->u.deflate->avail_in;
*olen_oused -= ctx->u.deflate->avail_out;
/* it's ambiguous with zlib... */
ctx->may_have_more = (*olen_oused == olen_oused_in);
return n == Z_STREAM_END;
}
static void
lcs_destroy_deflate(lws_comp_ctx_t *ctx)
{
if (!ctx)
return;
if (!(*ctx).is_decompression)
deflateEnd((*ctx).u.deflate);
else
inflateEnd((*ctx).u.deflate);
lws_free_set_NULL(ctx->u.deflate);
}
struct lws_compression_support lcs_deflate = {
/* .encoding_name */ "deflate",
/* .init_compression */ lcs_init_compression_deflate,
/* .process */ lcs_process_deflate,
/* .destroy */ lcs_destroy_deflate,
};

View File

@ -0,0 +1,87 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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.
*
* This is included from private-lib-core.h if LWS_WITH_HTTP_STREAM_COMPRESSION
*/
#if defined(LWS_WITH_MINIZ)
#include <miniz.h>
#else
#include <zlib.h>
#endif
#if defined(LWS_WITH_HTTP_BROTLI)
#include <brotli/encode.h>
#include <brotli/decode.h>
#endif
/*
* struct holding union of all the available compression methods' context data,
* and state if it's compressing or decompressing
*/
typedef struct lws_compression_ctx {
union {
#if defined(LWS_WITH_HTTP_BROTLI)
BrotliEncoderState *br_en;
BrotliDecoderState *br_de;
#endif
z_stream *deflate;
void *generic_ctx_ptr;
} u;
struct lws_buflist *buflist_comp;
unsigned int is_decompression:1;
unsigned int final_on_input_side:1;
unsigned int may_have_more:1;
unsigned int chunking:1;
} lws_comp_ctx_t;
/* generic structure defining the interface to a compression method */
struct lws_compression_support {
/** compression name as used by, eg, content-ecoding */
const char *encoding_name;
/** create a compression context for the compression method, or NULL */
int (*init_compression)(lws_comp_ctx_t *ctx, int decomp);
/** pass data into the context to be processed */
int (*process)(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,
void *out, size_t *olen_oused);
/** destroy the de/compression context */
void (*destroy)(lws_comp_ctx_t *ctx);
};
extern struct lws_compression_support lcs_deflate;
extern struct lws_compression_support lcs_brotli;
int
lws_http_compression_validate(struct lws *wsi);
int
lws_http_compression_transform(struct lws *wsi, unsigned char *buf,
size_t len, enum lws_write_protocol *wp,
unsigned char **outbuf, size_t *olen_oused);
void
lws_http_compression_destroy(struct lws *wsi);

View File

@ -0,0 +1,227 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
/* compression methods listed in order of preference */
struct lws_compression_support *lcs_available[] = {
#if defined(LWS_WITH_HTTP_BROTLI)
&lcs_brotli,
#endif
&lcs_deflate,
};
/* compute acceptable compression encodings while we still have an ah */
int
lws_http_compression_validate(struct lws *wsi)
{
const char *a;
size_t n;
wsi->http.comp_accept_mask = 0;
if (!wsi->http.ah || !lwsi_role_server(wsi))
return 0;
a = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING);
if (!a)
return 0;
for (n = 0; n < LWS_ARRAY_SIZE(lcs_available); n++)
if (strstr(a, lcs_available[n]->encoding_name))
wsi->http.comp_accept_mask = (uint8_t)(wsi->http.comp_accept_mask | (1 << n));
return 0;
}
int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp)
{
size_t n;
for (n = 0; n < LWS_ARRAY_SIZE(lcs_available); n++) {
/* if name is non-NULL, choose only that compression method */
if (name && strcmp(lcs_available[n]->encoding_name, name))
continue;
/*
* If we're the server, confirm that the client told us he could
* handle this kind of compression transform...
*/
if (!decomp && !(wsi->http.comp_accept_mask & (1 << n)))
continue;
/* let's go with this one then... */
break;
}
if (n == LWS_ARRAY_SIZE(lcs_available))
return 1;
lcs_available[n]->init_compression(&wsi->http.comp_ctx, decomp);
if (!wsi->http.comp_ctx.u.generic_ctx_ptr) {
lwsl_err("%s: init_compression %d failed\n", __func__, (int)n);
return 1;
}
wsi->http.lcs = lcs_available[n];
wsi->http.comp_ctx.may_have_more = 0;
wsi->http.comp_ctx.final_on_input_side = 0;
wsi->http.comp_ctx.chunking = 0;
wsi->http.comp_ctx.is_decompression = !!decomp;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING,
(unsigned char *)lcs_available[n]->encoding_name,
(int)strlen(lcs_available[n]->encoding_name), p, end))
return -1;
lwsl_info("%s: %s: applied %s content-encoding\n", __func__,
lws_wsi_tag(wsi), lcs_available[n]->encoding_name);
return 0;
}
void
lws_http_compression_destroy(struct lws *wsi)
{
if (!wsi->http.lcs || !wsi->http.comp_ctx.u.generic_ctx_ptr)
return;
wsi->http.lcs->destroy(&wsi->http.comp_ctx);
wsi->http.lcs = NULL;
}
/*
* This manages the compression transform independent of h1 or h2.
*
* wsi->buflist_comp stashes pre-transform input that was not yet compressed
*/
int
lws_http_compression_transform(struct lws *wsi, unsigned char *buf,
size_t len, enum lws_write_protocol *wp,
unsigned char **outbuf, size_t *olen_oused)
{
size_t ilen_iused = len;
int n, use = 0, wp1f = (*wp) & 0x1f;
lws_comp_ctx_t *ctx = &wsi->http.comp_ctx;
ctx->may_have_more = 0;
if (!wsi->http.lcs ||
(wp1f != LWS_WRITE_HTTP && wp1f != LWS_WRITE_HTTP_FINAL)) {
*outbuf = buf;
*olen_oused = len;
return 0;
}
if (wp1f == LWS_WRITE_HTTP_FINAL) {
/*
* ...we may get a large buffer that represents the final input
* buffer, but it may form multiple frames after being
* tranformed by compression; only the last of those is actually
* the final frame on the output stream.
*
* Note that we have received the FINAL input, and downgrade it
* to a non-final for now.
*/
ctx->final_on_input_side = 1;
*wp = (unsigned int)(LWS_WRITE_HTTP | ((*wp) & ~0x1fu));
}
if (ctx->buflist_comp) {
/*
* we can't send this new stuff when we have old stuff
* buffered and not compressed yet. Add it to the tail
* and switch to trying to process the head.
*/
if (buf && len) {
if (lws_buflist_append_segment(
&ctx->buflist_comp, buf, len) < 0)
return -1;
lwsl_debug("%s: %s: adding %d to comp buflist\n",
__func__, lws_wsi_tag(wsi), (int)len);
}
len = lws_buflist_next_segment_len(&ctx->buflist_comp, &buf);
ilen_iused = len;
use = 1;
lwsl_debug("%s: %s: trying comp buflist %d\n", __func__,
lws_wsi_tag(wsi), (int)len);
}
if (!buf && ilen_iused)
return 0;
lwsl_debug("%s: %s: pre-process: ilen_iused %d, olen_oused %d\n",
__func__, lws_wsi_tag(wsi), (int)ilen_iused, (int)*olen_oused);
n = wsi->http.lcs->process(ctx, buf, &ilen_iused, *outbuf, olen_oused);
if (n && n != 1) {
lwsl_err("%s: problem with compression\n", __func__);
return -1;
}
if (!ctx->may_have_more && ctx->final_on_input_side)
*wp = (unsigned int)(LWS_WRITE_HTTP_FINAL | ((*wp) & ~0x1fu));
lwsl_debug("%s: %s: more %d, ilen_iused %d\n", __func__, lws_wsi_tag(wsi),
ctx->may_have_more, (int)ilen_iused);
if (use && ilen_iused) {
/*
* we were flushing stuff from the buflist head... account for
* however much actually got processed by the compression
* transform
*/
lws_buflist_use_segment(&ctx->buflist_comp, ilen_iused);
lwsl_debug("%s: %s: marking %d of comp buflist as used "
"(ctx->buflist_comp %p)\n", __func__,
lws_wsi_tag(wsi), (int)len, ctx->buflist_comp);
}
if (!use && ilen_iused != len) {
/*
* ...we were sending stuff from the caller directly and not
* all of it got processed... stash on the buflist tail
*/
if (lws_buflist_append_segment(&ctx->buflist_comp,
buf + ilen_iused, len - ilen_iused) < 0)
return -1;
lwsl_debug("%s: buffering %d unused comp input\n", __func__,
(int)(len - ilen_iused));
}
if (ctx->buflist_comp || ctx->may_have_more)
lws_callback_on_writable(wsi);
return 0;
}

View File

@ -0,0 +1,735 @@
#include <libwebsockets.h>
#include "private-lib-core.h"
//#define LWS_COOKIE_DEBUG
#if defined(LWS_COOKIE_DEBUG)
#define lwsl_cookie lwsl_notice
#else
#define lwsl_cookie lwsl_debug
#endif
#define LWS_COOKIE_MAX_CACHE_NAME_LEN 128
#define lws_tolower(_c) (((_c) >= 'A' && (_c) <= 'Z') ? \
(char)((_c) + 'a' - 'A') : \
(char)(_c))
#define LWS_COOKIE_NSC_FORMAT "%.*s\t"\
"%s\t"\
"%.*s\t"\
"%s\t"\
"%llu\t"\
"%.*s\t"\
"%.*s"
static const char *const mon = "janfebmaraprnayjunjulaugsepoctnovdec";
enum lws_cookie_nsc_f {
LWSC_NSC_DOMAIN,
LWSC_NSC_HOSTONLY,
LWSC_NSC_PATH,
LWSC_NSC_SECURE,
LWSC_NSC_EXPIRES,
LWSC_NSC_NAME,
LWSC_NSC_VALUE,
LWSC_NSC_COUNT,
};
enum lws_cookie_elements {
CE_DOMAIN,
CE_PATH,
CE_EXPIRES,
CE_MAXAGE,
CE_NAME,
CE_VALUE,
CE_HOSTONLY, /* these are bool, NULL = 0, non-NULL = 1 */
CE_SECURE,
CE_COUNT
};
struct lws_cookie {
const char *f[CE_COUNT];
size_t l[CE_COUNT];
unsigned int httponly:1;
};
static int
lws_cookie_parse_date(const char *d, size_t len, time_t *t)
{
struct tm date;
int offset = 0, i;
memset(&date, 0, sizeof(date));
while (len) {
if (isalnum((int)*d)) {
offset++;
goto next;
}
switch (offset) {
case 2:
if (*d == ':' && len >= 6) {
date.tm_hour = atoi(d - 2);
if (date.tm_hour < 0 || date.tm_hour > 23)
return -1;
date.tm_min = atoi(d + 1);
if (date.tm_min < 0 || date.tm_min > 60)
return -1;
date.tm_sec = atoi(d + 4);
if (date.tm_sec < 0 || date.tm_sec > 61)
/* leap second */
return -1;
d += 6;
len -= 6;
offset = 0;
continue;
}
if (!date.tm_mday) {
date.tm_mday = atoi(d - 2);
if (date.tm_mday < 1 || date.tm_mday > 31)
return -1;
goto next2;
}
if (!date.tm_year) {
date.tm_year = atoi(d - 2);
if (date.tm_year < 0 || date.tm_year > 99)
return -1;
if (date.tm_year < 70)
date.tm_year += 100;
}
goto next2;
case 3:
for (i = 0; i < 36; i += 3) {
if (lws_tolower(*(d - 3)) == mon[i] &&
lws_tolower(*(d - 2)) == mon[i + 1] &&
lws_tolower(*(d - 1)) == mon[i + 2]) {
date.tm_mon = i / 3;
break;
}
}
goto next2;
case 4:
if (!date.tm_year) {
date.tm_year = atoi(d - 4);
if (date.tm_year < 1601)
return -1;
date.tm_year -= 1900;
}
goto next2;
default:
goto next2;
}
next2:
offset = 0;
next:
d++;
len--;
}
*t = mktime(&date);
if (*t < 0)
return -1;
return 0;
}
static void
lws_cookie_rm_sws(const char **buf_p, size_t *len_p)
{
const char *buf;
size_t len;
if (!buf_p || !*buf_p || !len_p || !*len_p) {
lwsl_err("%s: false parameter\n", __func__);
return;
}
buf = *buf_p;
len = *len_p;
while (buf[0] == ' ' && len > 0) {
buf++;
len--;
}
while (buf[len - 1] == ' ' && len > 0)
len--;
*buf_p = buf;
*len_p = len;
}
static int
is_iprefix(const char *h, size_t hl, const char *n, size_t nl)
{
if (!h || !n || nl > hl)
return 0;
while (nl) {
nl--;
if (lws_tolower(h[nl]) != lws_tolower(n[nl]))
return 0;
}
return 1;
}
static int
lws_cookie_compile_cache_name(char *buf, size_t buf_len, struct lws_cookie *c)
{
if (!buf || !c->f[CE_DOMAIN] || !c->f[CE_PATH] || !c->f[CE_NAME] ||
c->l[CE_DOMAIN] + c->l[CE_PATH] + c->l[CE_NAME] + 6 > buf_len)
return -1;
memcpy(buf, c->f[CE_DOMAIN], c->l[CE_DOMAIN]);
buf += c->l[CE_DOMAIN];
*buf++ = '|';
memcpy(buf, c->f[CE_PATH], c->l[CE_PATH]);
buf += c->l[CE_PATH];
*buf++ = '|';
memcpy(buf, c->f[CE_NAME], c->l[CE_NAME]);
buf += c->l[CE_NAME];
*buf = '\0';
return 0;
}
static int
lws_cookie_parse_nsc(struct lws_cookie *c, const char *b, size_t l)
{
enum lws_cookie_nsc_f state = LWSC_NSC_DOMAIN;
size_t n = 0;
if (!c || !b || l < 13)
return -1;
memset(c, 0, sizeof(*c));
lwsl_cookie("%s: parsing (%.*s) \n", __func__, (int)l, b);
while (l) {
l--;
if (b[n] != '\t' && l) {
n++;
continue;
}
switch (state) {
case LWSC_NSC_DOMAIN:
c->f[CE_DOMAIN] = b;
c->l[CE_DOMAIN] = n;
break;
case LWSC_NSC_PATH:
c->f[CE_PATH] = b;
c->l[CE_PATH] = n;
break;
case LWSC_NSC_EXPIRES:
c->f[CE_EXPIRES] = b;
c->l[CE_EXPIRES] = n;
break;
case LWSC_NSC_NAME:
c->f[CE_NAME] = b;
c->l[CE_NAME] = n;
break;
case LWSC_NSC_HOSTONLY:
if (b[0] == 'T') {
c->f[CE_HOSTONLY] = b;
c->l[CE_HOSTONLY] = 1;
}
break;
case LWSC_NSC_SECURE:
if (b[0] == 'T') {
c->f[CE_SECURE] = b;
c->l[CE_SECURE] = 1;
}
break;
case LWSC_NSC_VALUE:
c->f[CE_VALUE] = b;
c->l[CE_VALUE] = n + 1;
for (n = 0; n < LWS_ARRAY_SIZE(c->f); n++)
lwsl_cookie("%s: %d: %.*s\n", __func__,
(int)n, (int)c->l[n], c->f[n]);
return 0;
default:
return -1;
}
b += n + 1;
n = 0;
state++;
}
return -1;
}
static int
lws_cookie_write_nsc(struct lws *wsi, struct lws_cookie *c)
{
char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
const char *ads, *path;
struct lws_cache_ttl_lru *l1;
struct client_info_stash *stash;
char *cookie_string = NULL, *dl;
/* 6 tabs + 20 for max time_t + 2 * TRUE/FALSE + null */
size_t size = 6 + 20 + 10 + 1;
time_t expires = 0;
int ret = 0;
if (!wsi || !c)
return -1;
l1 = wsi->a.context->l1;
if (!l1 || !wsi->a.context->nsc)
return -1;
stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
if (stash) {
ads = stash->cis[CIS_ADDRESS];
path = stash->cis[CIS_PATH];
} else {
ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
path = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
}
if (!ads || !path)
return -1;
if (!c->f[CE_NAME] || !c->f[CE_VALUE]) {
lwsl_err("%s: malformed c\n", __func__);
return -1;
}
if (!c->f[CE_EXPIRES]) {
/*
* Currently we just take the approach to reject session cookies
*/
lwsl_warn("%s: reject session cookies\n", __func__);
return 0;
}
if (!c->f[CE_DOMAIN]) {
c->f[CE_HOSTONLY] = "T";
c->l[CE_HOSTONLY] = 1;
c->f[CE_DOMAIN] = ads;
c->l[CE_DOMAIN] = strlen(ads);
}
if (!c->f[CE_PATH]) {
c->f[CE_PATH] = path;
c->l[CE_PATH] = strlen(path);
dl = memchr(c->f[CE_PATH], '?', c->l[CE_PATH]);
if (dl)
c->l[CE_PATH] = (size_t)(dl - c->f[CE_PATH]);
}
if (lws_cookie_compile_cache_name(cache_name, sizeof(cache_name), c))
return -1;
if (c->f[CE_EXPIRES] &&
lws_cookie_parse_date(c->f[CE_EXPIRES], c->l[CE_EXPIRES], &expires)) {
lwsl_err("%s: can't parse date %.*s\n", __func__,
(int)c->l[CE_EXPIRES], c->f[CE_EXPIRES]);
return -1;
}
size += c->l[CE_NAME] + c->l[CE_VALUE] + c->l[CE_DOMAIN] + c->l[CE_PATH];
cookie_string = (char *)lws_malloc(size, __func__);
if (!cookie_string) {
lwsl_err("%s: OOM\n",__func__);
return -1;
}
lws_snprintf(cookie_string, size, LWS_COOKIE_NSC_FORMAT,
(int)c->l[CE_DOMAIN], c->f[CE_DOMAIN],
c->f[CE_HOSTONLY] ? "TRUE" : "FALSE",
(int)c->l[CE_PATH], c->f[CE_PATH],
c->f[CE_SECURE] ? "TRUE" : "FALSE",
(unsigned long long)expires,
(int)c->l[CE_NAME], c->f[CE_NAME],
(int)c->l[CE_VALUE], c->f[CE_VALUE]);
lwsl_cookie("%s: name %s\n", __func__, cache_name);
lwsl_cookie("%s: c %s\n", __func__, cookie_string);
if (lws_cache_write_through(l1, cache_name,
(const uint8_t *)cookie_string,
strlen(cookie_string),
(lws_usec_t)((unsigned long long)expires *
(lws_usec_t)LWS_US_PER_SEC), NULL)) {
ret = -1;
goto exit;
}
#if defined(LWS_COOKIE_DEBUG)
char *po;
if (lws_cache_item_get(l1, cache_name, (const void **)&po, &size) ||
size != strlen(cookie_string) || memcmp(po, cookie_string, size)) {
lwsl_err("%s: L1 '%s' missing\n", __func__, cache_name);
}
if (lws_cache_item_get(wsi->a.context->nsc, cache_name,
(const void **)&po, &size) ||
size != strlen(cookie_string) ||
memcmp(po, cookie_string, size)) {
lwsl_err("%s: NSC '%s' missing, size %llu, po %s\n", __func__,
cache_name, (unsigned long long)size, po);
}
#endif
exit:
lws_free(cookie_string);
return ret;
}
static int
lws_cookie_attach_cookies(struct lws *wsi, char *buf, char *end)
{
const char *domain, *path, *dl_domain, *dl_path, *po;
char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
size_t domain_len, path_len, size, ret = 0;
struct lws_cache_ttl_lru *l1;
struct client_info_stash *stash;
lws_cache_results_t cr;
struct lws_cookie c;
int hostdomain = 1;
char *p, *p1;
if (!wsi)
return -1;
stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
if (!stash || !stash->cis[CIS_ADDRESS] ||
!stash->cis[CIS_PATH])
return -1;
l1 = wsi->a.context->l1;
if (!l1 || !wsi->a.context->nsc){
lwsl_err("%s:no cookiejar\n", __func__);
return -1;
}
memset(&c, 0, sizeof(c));
domain = stash->cis[CIS_ADDRESS];
path = stash->cis[CIS_PATH];
if (!domain || !path)
return -1;
path_len = strlen(path);
/* remove query string if exist */
dl_path = memchr(path, '?', path_len);
if (dl_path)
path_len = lws_ptr_diff_size_t(dl_path, path);
/* remove last slash if exist */
if (path_len != 1 && path[path_len - 1] == '/')
path_len--;
if (!path_len)
return -1;
lwsl_cookie("%s: path %.*s len %d\n", __func__, (int)path_len, path, (int)path_len);
/* when dest buf is not provided, we only return size of cookie string */
if (!buf || !end)
p = NULL;
else
p = buf;
/* iterate through domain and path levels to find matching cookies */
dl_domain = domain;
while (dl_domain) {
domain_len = strlen(domain);
dl_domain = memchr(domain, '.', domain_len);
/* don't match top level domain */
if (!dl_domain)
break;
if (domain_len + path_len + 6 > sizeof(cache_name))
return -1;
/* compile key string "[domain]|[path]|*"" */
p1 = cache_name;
memcpy(p1, domain, domain_len);
p1 += domain_len;
*p1 = '|';
p1++;
memcpy(p1, path, path_len);
p1 += path_len;
*p1 = '|';
p1++;
*p1 = '*';
p1++;
*p1 = '\0';
lwsl_cookie("%s: looking for %s\n", __func__, cache_name);
if (!lws_cache_lookup(l1, cache_name,
(const void **)&cr.ptr, &cr.size)) {
while (!lws_cache_results_walk(&cr)) {
lwsl_cookie(" %s (%d)\n", (const char *)cr.tag,
(int)cr.payload_len);
if (lws_cache_item_get(l1, (const char *)cr.tag,
(const void **)&po, &size) ||
lws_cookie_parse_nsc(&c, po, size)) {
lwsl_err("%s: failed to get c '%s'\n",
__func__, cr.tag);
break;
}
if (c.f[CE_HOSTONLY] && !hostdomain){
lwsl_cookie("%s: not sending this\n",
__func__);
continue;
}
if (p) {
if (ret) {
*p = ';';
p++;
*p = ' ';
p++;
}
memcpy(p, c.f[CE_NAME], c.l[CE_NAME]);
p += c.l[CE_NAME];
*p = '=';
p++;
memcpy(p, c.f[CE_VALUE], c.l[CE_VALUE]);
p += c.l[CE_VALUE];
}
if (ret)
ret += 2;
ret += c.l[CE_NAME] + 1 + c.l[CE_VALUE];
}
}
domain = dl_domain + 1;
hostdomain = 0;
}
lwsl_notice("%s: c len (%d)\n", __func__, (int)ret);
return (int)ret;
}
static struct {
const char *const name;
uint8_t len;
} cft[] = {
{ "domain=", 7 },
{ "path=", 5 },
{ "expires=", 8 },
{ "max-age=", 8 },
{ "httponly", 8 },
{ "secure", 6 }
};
int
lws_parse_set_cookie(struct lws *wsi)
{
char *tk_head, *tk_end, *buf_head, *buf_end, *cookiep, *dl;
struct lws_cache_ttl_lru *l1;
struct lws_cookie c;
size_t fl;
int f, n;
if (!wsi)
return -1;
l1 = wsi->a.context->l1;
if (!l1)
return -1;
f = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_SET_COOKIE];
while (f) {
cookiep = wsi->http.ah->data + wsi->http.ah->frags[f].offset;
fl = wsi->http.ah->frags[f].len;
f = wsi->http.ah->frags[f].nfrag;
if (!cookiep || !fl)
continue;
#if defined(LWS_COOKIE_DEBUG)
lwsl_notice("%s:parsing: %.*s\n", __func__, (int)fl, cookiep);
#endif
buf_head = cookiep;
buf_end = cookiep + fl - 1;
memset(&c, 0, sizeof(struct lws_cookie));
do {
tk_head = buf_head;
tk_end = memchr(buf_head, ';',
(size_t)(buf_end - buf_head + 1));
if (!tk_end) {
tk_end = buf_end;
buf_head = buf_end;
} else {
buf_head = tk_end + 1;
tk_end--;
}
if (c.f[CE_NAME])
goto parse_av;
/*
* find name value, remove leading trailing
* WS and DQ for value
*/
dl = memchr(tk_head, '=', lws_ptr_diff_size_t(tk_end,
tk_head + 1));
if (!dl || dl == tk_head)
return -1;
c.f[CE_NAME] = tk_head;
c.l[CE_NAME] = lws_ptr_diff_size_t(dl, tk_head);
lws_cookie_rm_sws(&c.f[CE_NAME], &c.l[CE_NAME]);
if (!c.l[CE_NAME])
return -1;
lwsl_cookie("%s: c name l %d v:%.*s\n", __func__,
(int)c.l[CE_NAME],
(int)c.l[CE_NAME], c.f[CE_NAME]);
c.f[CE_VALUE] = dl + 1;
c.l[CE_VALUE] = lws_ptr_diff_size_t(tk_end,
c.f[CE_VALUE]) + 1;
lws_cookie_rm_sws(&c.f[CE_VALUE], &c.l[CE_VALUE]);
if (c.l[CE_VALUE] >= 2 && c.f[CE_VALUE][0] == '\"') {
c.f[CE_VALUE]++;
c.l[CE_VALUE] -= 2;
}
lwsl_cookie("%s: c value l %d v:%.*s\n", __func__,
(int)c.l[CE_VALUE], (int)c.l[CE_VALUE],
c.f[CE_VALUE]);
continue;
parse_av:
while (*tk_head == ' ') {
if (tk_head == tk_end)
return -1;
tk_head++;
}
for (n = 0; n < (int)LWS_ARRAY_SIZE(cft); n++) {
if (lws_tolower(*tk_head) != cft[n].name[0])
continue;
if (!is_iprefix(tk_head,
lws_ptr_diff_size_t(tk_end,
tk_head) + 1,
cft[n].name, cft[n].len))
continue;
if (n == 4 || n == 5) {
c.f[n] = "T";
c.l[n] = 1;
break;
}
c.f[n] = tk_head + cft[n].len;
c.l[n] = lws_ptr_diff_size_t(tk_end, c.f[n]) + 1;
lws_cookie_rm_sws(&c.f[n], &c.l[n]);
if (n == CE_DOMAIN && c.l[0] &&
c.f[n][0] == '.'){
c.f[n]++;
c.l[n]--;
}
lwsl_cookie("%s: %s l %d v:%.*s\n", __func__,
cft[n].name, (int)c.l[n],
(int)c.l[n], c.f[n]);
break;
}
} while (tk_end != buf_end);
if (lws_cookie_write_nsc(wsi, &c))
lwsl_err("%s:failed to write nsc\n", __func__);
}
return 0;
}
int
lws_cookie_send_cookies(struct lws *wsi, char **pp, char *end)
{
char *p;
int size;
if (!wsi || !pp || !(*pp) || !end)
return -1;
size = lws_cookie_attach_cookies(wsi, NULL, NULL);
if (!size)
return 0;
if (size < 0) {
lwsl_err("%s:failed to get cookie string size\n", __func__);
return -1;
}
lwsl_notice("%s: size %d\n", __func__, size);
#if defined(LWS_COOKIE_DEBUG)
char *p_dbg = *pp;
#endif
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE, NULL, size,
(unsigned char **)pp, (unsigned char *)end))
return -1;
#if defined(LWS_COOKIE_DEBUG)
lwsl_notice("%s: dummy copy (%.*s) \n", __func__, (int)(*pp - p_dbg), p_dbg);
#endif
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi))
p = *pp - size;
else
#endif
p = *pp - size - 2;
if (lws_cookie_attach_cookies(wsi, p, p + size) <= 0) {
lwsl_err("%s:failed to attach cookies\n", __func__);
return -1;
}
#if defined(LWS_COOKIE_DEBUG)
lwsl_notice("%s: real copy (%.*s) total len %d\n", __func__, (int)(*pp - p_dbg), p_dbg, (int)(*pp - p_dbg));
lwsl_hexdump_notice(p_dbg, (size_t)(*pp - p_dbg));
#endif
return 0;
}

View File

@ -0,0 +1,225 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* 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.
*
* RFC7231 date string generation and parsing
*/
#include "private-lib-core.h"
/*
* To avoid needless pointers, we encode these in one string using the fact
* they're 3 chars each to index it
*/
static const char *const s =
"JanFebMarAprMayJunJulAugSepOctNovDecMonTueWedThuFriSatSun";
static int
lws_http_date_render(char *buf, size_t len, const struct tm *tm)
{
const char *w = s + 36 + (3 * tm->tm_wday), *m = s + (3 * tm->tm_mon);
if (len < 29)
return -1;
lws_snprintf(buf, len, "%c%c%c, %02d %c%c%c %d %02d:%02d:%02d GMT",
w[0], w[1], w[2], tm->tm_mday, m[0], m[1], m[2],
1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
return 0;
}
int
lws_http_date_render_from_unix(char *buf, size_t len, const time_t *t)
{
#if defined(LWS_HAVE_GMTIME_R)
struct tm tmp;
struct tm *tm = gmtime_r(t, &tmp);
#else
struct tm *tm = gmtime(t);
#endif
if (!tm)
return -1;
if (lws_http_date_render(buf, len, tm))
return -1;
return 0;
}
static int
lws_http_date_parse(const char *b, size_t len, struct tm *tm)
{
int n;
if (len < 29)
return -1;
/*
* We reject anything that isn't a properly-formatted RFC7231 date, eg
*
* Tue, 15 Nov 1994 08:12:31 GMT
*/
if (b[3] != ',' || b[4] != ' ' || b[7] != ' ' || b[11] != ' ' ||
b[16] != ' ' || b[19] != ':' || b[22] != ':' || b[25] != ' ' ||
b[26] != 'G' || b[27] != 'M' || b[28] != 'T')
return -1;
memset(tm, 0, sizeof(*tm));
for (n = 36; n < 57; n += 3)
if (b[0] == s[n] && b[1] == s[n + 1] && b[2] == s[n + 2])
break;
else
tm->tm_wday++;
if (n == 57)
return -1;
for (n = 0; n < 36; n += 3)
if (b[8] == s[n] && b[9] == s[n + 1] && b[10] == s[n + 2])
break;
else
tm->tm_mon++;
if (n == 36)
return -1;
tm->tm_mday = atoi(b + 5);
n = atoi(b + 12);
if (n < 1900)
return -1;
tm->tm_year = n - 1900;
n = atoi(b + 17);
if (n < 0 || n > 23)
return -1;
tm->tm_hour = n;
n = atoi(b + 20);
if (n < 0 || n > 60)
return -1;
tm->tm_min = n;
n = atoi(b + 23);
if (n < 0 || n > 61) /* leap second */
return -1;
tm->tm_sec = n;
return 0;
}
int
lws_http_date_parse_unix(const char *b, size_t len, time_t *t)
{
struct tm tm;
if (lws_http_date_parse(b, len, &tm))
return -1;
#if defined(WIN32)
*t = _mkgmtime(&tm);
#else
#if defined(LWS_HAVE_TIMEGM)
*t = timegm(&tm);
#else
/* this is a poor fallback since it uses localtime zone */
*t = mktime(&tm);
#endif
#endif
return (int)(*t == -1 ? -1 : 0);
}
#if defined(LWS_WITH_CLIENT)
int
lws_http_check_retry_after(struct lws *wsi, lws_usec_t *us_interval_in_out)
{
size_t len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_RETRY_AFTER);
char *p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_RETRY_AFTER);
lws_usec_t u;
time_t t, td;
if (!p)
return 1;
/*
* There are two arg styles for RETRY_AFTER specified in RFC7231 7.1.3,
* either a full absolute second-resolution date/time, or an integer
* interval
*
* Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
* Retry-After: 120
*/
if (len < 9)
u = ((lws_usec_t)(time_t)atoi(p)) * LWS_USEC_PER_SEC;
else {
if (lws_http_date_parse_unix(p, len, &t))
return 1;
/*
* If possible, look for DATE from the server as well, so we
* can calculate the interval it thinks it is giving us,
* eliminating problems from server - client clock skew
*/
time(&td);
len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_DATE);
if (len) {
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_DATE);
/* if this fails, it leaves td as client time */
(void)lws_http_date_parse_unix(p, len, &td);
}
if (td >= t)
/*
* if he's effectively giving us a 0 or negative
* interval, just ignore the whole thing and keep the
* incoming interval
*/
return 1;
u = ((lws_usec_t)(t - td)) * LWS_USEC_PER_SEC;
}
/*
* We are only willing to increase the incoming interval, not
* decrease it
*/
if (u < *us_interval_in_out)
/* keep the incoming interval */
return 1;
/* use the computed interval */
*us_interval_in_out = u;
return 0;
}
#endif

View File

@ -0,0 +1,666 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "lextable-strings.h"
const unsigned char *
lws_token_to_string(enum lws_token_indexes token)
{
if ((unsigned int)token >= LWS_ARRAY_SIZE(set))
return NULL;
return (unsigned char *)set[token];
}
/*
* Return http header index if one matches slen chars of s, or -1
*/
int
lws_http_string_to_known_header(const char *s, size_t slen)
{
int n;
for (n = 0; n < (int)LWS_ARRAY_SIZE(set); n++)
if (!strncmp(set[n], s, slen))
return n;
return LWS_HTTP_NO_KNOWN_HEADER;
}
#ifdef LWS_WITH_HTTP2
int
lws_wsi_is_h2(struct lws *wsi)
{
return wsi->upgraded_to_http2 ||
wsi->mux_substream ||
#if defined(LWS_WITH_CLIENT)
wsi->client_mux_substream ||
#endif
lwsi_role_h2(wsi) ||
lwsi_role_h2_ENCAPSULATION(wsi);
}
#endif
int
lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
{
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi))
return lws_add_http2_header_by_name(wsi, name,
value, length, p, end);
#else
(void)wsi;
#endif
if (name) {
char has_colon = 0;
while (*p < end && *name) {
has_colon = has_colon || *name == ':';
*((*p)++) = *name++;
}
if (*p + (has_colon ? 1 : 2) >= end)
return 1;
if (!has_colon)
*((*p)++) = ':';
*((*p)++) = ' ';
}
if (*p + length + 3 >= end)
return 1;
if (value)
memcpy(*p, value, (unsigned int)length);
*p += length;
*((*p)++) = '\x0d';
*((*p)++) = '\x0a';
return 0;
}
int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
unsigned char *end)
{
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi))
return 0;
#else
(void)wsi;
#endif
if ((lws_intptr_t)(end - *p) < 3)
return 1;
*((*p)++) = '\x0d';
*((*p)++) = '\x0a';
return 0;
}
int
lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
unsigned char **pp, unsigned char *end)
{
unsigned char *p;
int len;
if (lws_finalize_http_header(wsi, pp, end))
return 1;
p = *pp;
len = lws_ptr_diff(p, start);
if (lws_write(wsi, start, (unsigned int)len, LWS_WRITE_HTTP_HEADERS) != len)
return 1;
return 0;
}
int
lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
{
const unsigned char *name;
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi))
return lws_add_http2_header_by_token(wsi, token, value,
length, p, end);
#endif
name = lws_token_to_string(token);
if (!name)
return 1;
return lws_add_http_header_by_name(wsi, name, value, length, p, end);
}
int
lws_add_http_header_content_length(struct lws *wsi,
lws_filepos_t content_length,
unsigned char **p, unsigned char *end)
{
char b[24];
int n;
n = lws_snprintf(b, sizeof(b) - 1, "%llu", (unsigned long long)content_length);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)b, n, p, end))
return 1;
wsi->http.tx_content_length = content_length;
wsi->http.tx_content_remain = content_length;
lwsl_info("%s: %s: tx_content_length/remain %llu\n", __func__,
lws_wsi_tag(wsi), (unsigned long long)content_length);
return 0;
}
#if defined(LWS_WITH_SERVER)
int
lws_add_http_common_headers(struct lws *wsi, unsigned int code,
const char *content_type, lws_filepos_t content_len,
unsigned char **p, unsigned char *end)
{
const char *ka[] = { "close", "keep-alive" };
int types[] = { HTTP_CONNECTION_CLOSE, HTTP_CONNECTION_KEEP_ALIVE },
t = 0;
if (lws_add_http_header_status(wsi, code, p, end))
return 1;
if (content_type &&
lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)content_type,
(int)strlen(content_type), p, end))
return 1;
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
if (!wsi->http.lcs && content_type &&
(!strncmp(content_type, "text/", 5) ||
!strcmp(content_type, "application/javascript") ||
!strcmp(content_type, "image/svg+xml")))
lws_http_compression_apply(wsi, NULL, p, end, 0);
#endif
/*
* if we decided to compress it, we don't know the content length...
* the compressed data will go out chunked on h1
*/
if (
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
!wsi->http.lcs &&
#endif
content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN) {
if (lws_add_http_header_content_length(wsi, content_len,
p, end))
return 1;
} else {
/* there was no length... it normally means CONNECTION_CLOSE */
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
if (!wsi->mux_substream && wsi->http.lcs) {
/* so...
* - h1 connection
* - http compression transform active
* - did not send content length
*
* then mark as chunked...
*/
wsi->http.comp_ctx.chunking = 1;
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
(unsigned char *)"chunked", 7, p, end))
return -1;
/* ... but h1 compression is chunked, if active we can
* still pipeline
*/
if (wsi->http.lcs &&
wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE)
t = 1;
}
#endif
if (!wsi->mux_substream) {
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_CONNECTION,
(unsigned char *)ka[t],
(int)strlen(ka[t]), p, end))
return 1;
wsi->http.conn_type = (enum http_conn_type)types[t];
}
}
return 0;
}
static const char * const err400[] = {
"Bad Request",
"Unauthorized",
"Payment Required",
"Forbidden",
"Not Found",
"Method Not Allowed",
"Not Acceptable",
"Proxy Auth Required",
"Request Timeout",
"Conflict",
"Gone",
"Length Required",
"Precondition Failed",
"Request Entity Too Large",
"Request URI too Long",
"Unsupported Media Type",
"Requested Range Not Satisfiable",
"Expectation Failed"
};
static const char * const err500[] = {
"Internal Server Error",
"Not Implemented",
"Bad Gateway",
"Service Unavailable",
"Gateway Timeout",
"HTTP Version Not Supported"
};
/* security best practices from Mozilla Observatory */
static const
struct lws_protocol_vhost_options pvo_hsbph[] = {{
NULL, NULL, "referrer-policy:", "no-referrer"
}, {
&pvo_hsbph[0], NULL, "x-frame-options:", "deny"
}, {
&pvo_hsbph[1], NULL, "x-xss-protection:", "1; mode=block"
}, {
&pvo_hsbph[2], NULL, "x-content-type-options:", "nosniff"
}, {
&pvo_hsbph[3], NULL, "content-security-policy:",
"default-src 'none'; img-src 'self' data: ; "
"script-src 'self'; font-src 'self'; "
"style-src 'self'; connect-src 'self' ws: wss:; "
"frame-ancestors 'none'; base-uri 'none';"
"form-action 'self';"
}};
int
lws_add_http_header_status(struct lws *wsi, unsigned int _code,
unsigned char **p, unsigned char *end)
{
static const char * const hver[] = {
"HTTP/1.0", "HTTP/1.1", "HTTP/2"
};
const struct lws_protocol_vhost_options *headers;
unsigned int code = _code & LWSAHH_CODE_MASK;
const char *description = "", *p1;
unsigned char code_and_desc[60];
int n;
wsi->http.response_code = code;
#ifdef LWS_WITH_ACCESS_LOG
wsi->http.access_log.response = (int)code;
#endif
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi)) {
n = lws_add_http2_header_status(wsi, code, p, end);
if (n)
return n;
} else
#endif
{
if (code >= 400 && code < (400 + LWS_ARRAY_SIZE(err400)))
description = err400[code - 400];
if (code >= 500 && code < (500 + LWS_ARRAY_SIZE(err500)))
description = err500[code - 500];
if (code == 100)
description = "Continue";
if (code == 200)
description = "OK";
if (code == 304)
description = "Not Modified";
else
if (code >= 300 && code < 400)
description = "Redirect";
if (wsi->http.request_version < LWS_ARRAY_SIZE(hver))
p1 = hver[wsi->http.request_version];
else
p1 = hver[0];
n = lws_snprintf((char *)code_and_desc,
sizeof(code_and_desc) - 1, "%s %u %s",
p1, code, description);
if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p,
end))
return 1;
}
headers = wsi->a.vhost->headers;
while (headers) {
if (lws_add_http_header_by_name(wsi,
(const unsigned char *)headers->name,
(unsigned char *)headers->value,
(int)strlen(headers->value), p, end))
return 1;
headers = headers->next;
}
if (wsi->a.vhost->options &
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE) {
headers = &pvo_hsbph[LWS_ARRAY_SIZE(pvo_hsbph) - 1];
while (headers) {
if (lws_add_http_header_by_name(wsi,
(const unsigned char *)headers->name,
(unsigned char *)headers->value,
(int)strlen(headers->value), p, end))
return 1;
headers = headers->next;
}
}
if (wsi->a.context->server_string &&
!(_code & LWSAHH_FLAG_NO_SERVER_NAME)) {
assert(wsi->a.context->server_string_len > 0);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
(unsigned char *)wsi->a.context->server_string,
wsi->a.context->server_string_len, p, end))
return 1;
}
if (wsi->a.vhost->options & LWS_SERVER_OPTION_STS)
if (lws_add_http_header_by_name(wsi, (unsigned char *)
"Strict-Transport-Security:",
(unsigned char *)"max-age=15768000 ; "
"includeSubDomains", 36, p, end))
return 1;
if (*p >= (end - 2)) {
lwsl_err("%s: reached end of buffer\n", __func__);
return 1;
}
return 0;
}
int
lws_return_http_status(struct lws *wsi, unsigned int code,
const char *html_body)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
unsigned char *p = pt->serv_buf + LWS_PRE;
unsigned char *start = p;
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
char *body = (char *)start + context->pt_serv_buf_size - 512;
int n = 0, m = 0, len;
char slen[20];
if (!wsi->a.vhost) {
lwsl_err("%s: wsi not bound to vhost\n", __func__);
return 1;
}
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
if (!wsi->handling_404 &&
wsi->a.vhost->http.error_document_404 &&
code == HTTP_STATUS_NOT_FOUND)
/* we should do a redirect, and do the 404 there */
if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
(uint8_t *)wsi->a.vhost->http.error_document_404,
(int)strlen(wsi->a.vhost->http.error_document_404),
&p, end) > 0)
return 0;
#endif
/* if the redirect failed, just do a simple status */
p = start;
if (!html_body)
html_body = "";
if (lws_add_http_header_status(wsi, code, &p, end))
return 1;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)"text/html", 9,
&p, end))
return 1;
len = lws_snprintf(body, 510, "<html><head>"
"<meta charset=utf-8 http-equiv=\"Content-Language\" "
"content=\"en\"/>"
"<link rel=\"stylesheet\" type=\"text/css\" "
"href=\"/error.css\"/>"
"</head><body><h1>%u</h1>%s</body></html>", code, html_body);
n = lws_snprintf(slen, 12, "%d", len);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)slen, n, &p, end))
return 1;
if (lws_finalize_http_header(wsi, &p, end))
return 1;
#if defined(LWS_WITH_HTTP2)
if (wsi->mux_substream) {
/*
* for HTTP/2, the headers must be sent separately, since they
* go out in their own frame. That puts us in a bind that
* we won't always be able to get away with two lws_write()s in
* sequence, since the first may use up the writability due to
* the pipe being choked or SSL_WANT_.
*
* However we do need to send the human-readable body, and the
* END_STREAM.
*
* Solve it by writing the headers now...
*/
m = lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
LWS_WRITE_HTTP_HEADERS);
if (m != lws_ptr_diff(p, start))
return 1;
/*
* ... but stash the body and send it as a priority next
* handle_POLLOUT
*/
wsi->http.tx_content_length = (unsigned int)len;
wsi->http.tx_content_remain = (unsigned int)len;
wsi->h2.pending_status_body = lws_malloc((unsigned int)len + LWS_PRE + 1,
"pending status body");
if (!wsi->h2.pending_status_body)
return -1;
strcpy(wsi->h2.pending_status_body + LWS_PRE, body);
lws_callback_on_writable(wsi);
return 0;
} else
#endif
{
/*
* for http/1, we can just append the body after the finalized
* headers and send it all in one go.
*/
n = lws_ptr_diff(p, start) + len;
memcpy(p, body, (unsigned int)len);
m = lws_write(wsi, start, (unsigned int)n, LWS_WRITE_HTTP);
if (m != n)
return 1;
}
return m != n;
}
int
lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
unsigned char **p, unsigned char *end)
{
unsigned char *start = *p;
if (lws_add_http_header_status(wsi, (unsigned int)code, p, end))
return -1;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, loc, len,
p, end))
return -1;
/*
* if we're going with http/1.1 and keepalive, we have to give fake
* content metadata so the client knows we completed the transaction and
* it can do the redirect...
*/
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)"text/html", 9, p,
end))
return -1;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)"0", 1, p, end))
return -1;
if (lws_finalize_http_header(wsi, p, end))
return -1;
return lws_write(wsi, start, lws_ptr_diff_size_t(*p, start),
LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
}
#endif
#if !defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp)
{
(void)wsi;
(void)name;
(void)p;
(void)end;
(void)decomp;
return 0;
}
#endif
int
lws_http_headers_detach(struct lws *wsi)
{
return lws_header_table_detach(wsi, 0);
}
#if defined(LWS_WITH_SERVER)
void
lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t *sul)
{
struct allocated_headers *ah;
struct lws_context_per_thread *pt = lws_container_of(sul,
struct lws_context_per_thread, sul_ah_lifecheck);
struct lws *wsi;
time_t now;
int m;
now = time(NULL);
lws_pt_lock(pt, __func__);
ah = pt->http.ah_list;
while (ah) {
int len;
char buf[256];
const unsigned char *c;
if (!ah->in_use || !ah->wsi || !ah->assigned ||
(ah->wsi->a.vhost &&
(now - ah->assigned) <
ah->wsi->a.vhost->timeout_secs_ah_idle + 360)) {
ah = ah->next;
continue;
}
/*
* a single ah session somehow got held for
* an unreasonable amount of time.
*
* Dump info on the connection...
*/
wsi = ah->wsi;
buf[0] = '\0';
#if !defined(LWS_PLAT_OPTEE)
lws_get_peer_simple(wsi, buf, sizeof(buf));
#else
buf[0] = '\0';
#endif
lwsl_notice("%s: ah excessive hold: wsi %p\n"
" peer address: %s\n"
" ah pos %lu\n", __func__, lws_wsi_tag(wsi),
buf, (unsigned long)ah->pos);
buf[0] = '\0';
m = 0;
do {
c = lws_token_to_string((enum lws_token_indexes)m);
if (!c)
break;
if (!(*c))
break;
len = lws_hdr_total_length(wsi, (enum lws_token_indexes)m);
if (!len || len > (int)sizeof(buf) - 1) {
m++;
continue;
}
if (lws_hdr_copy(wsi, buf, sizeof buf, (enum lws_token_indexes)m) > 0) {
buf[sizeof(buf) - 1] = '\0';
lwsl_notice(" %s = %s\n",
(const char *)c, buf);
}
m++;
} while (1);
/* explicitly detach the ah */
lws_header_table_detach(wsi, 0);
/* ... and then drop the connection */
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"excessive ah");
ah = pt->http.ah_list;
}
lws_pt_unlock(pt);
}
#endif

View File

@ -0,0 +1,136 @@
/* set of parsable strings -- ALL LOWER CASE */
static const char * const set[] = {
"get ",
"post ",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
"options ",
#endif
"host:",
"connection:",
"upgrade:",
"origin:",
#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL)
"sec-websocket-draft:",
#endif
"\x0d\x0a",
#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL)
"sec-websocket-extensions:",
"sec-websocket-key1:",
"sec-websocket-key2:",
"sec-websocket-protocol:",
"sec-websocket-accept:",
"sec-websocket-nonce:",
#endif
"http/1.1 ",
#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"http2-settings:",
#endif
"accept:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
"access-control-request-headers:",
#endif
"if-modified-since:",
"if-none-match:",
"accept-encoding:",
"accept-language:",
"pragma:",
"cache-control:",
"authorization:",
"cookie:",
"content-length:",
"content-type:",
"date:",
"range:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"referer:",
#endif
#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL)
"sec-websocket-key:",
"sec-websocket-version:",
"sec-websocket-origin:",
#endif
#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
":authority",
":method",
":path",
":scheme",
":status",
#endif
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"accept-charset:",
#endif
"accept-ranges:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"access-control-allow-origin:",
#endif
"age:",
"allow:",
"content-disposition:",
"content-encoding:",
"content-language:",
"content-location:",
"content-range:",
"etag:",
"expect:",
"expires:",
"from:",
"if-match:",
"if-range:",
"if-unmodified-since:",
"last-modified:",
"link:",
"location:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"max-forwards:",
"proxy-authenticate:",
"proxy-authorization:",
#endif
"refresh:",
"retry-after:",
"server:",
"set-cookie:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"strict-transport-security:",
#endif
"transfer-encoding:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"user-agent:",
"vary:",
"via:",
"www-authenticate:",
#endif
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
"patch",
"put",
"delete",
#endif
"uri-args", /* fake header used for uri-only storage */
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
"proxy ",
"x-real-ip:",
#endif
"http/1.0 ",
"x-forwarded-for:",
"connect ",
"head ",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"te:", /* http/2 wants it to reject it */
"replay-nonce:", /* ACME */
#endif
#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
":protocol", /* defined in mcmanus-httpbis-h2-ws-02 */
#endif
"x-auth-token:",
"x-amzn-dss-signature:",
"", /* not matchable */
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,500 @@
/*
* minilex.c
*
* High efficiency lexical state parser
*
* Copyright (C)2011-2020 Andy Green <andy@warmcat.com>
*
* Licensed under MIT
*
* Usage: gcc minilex.c -o minilex && ./minilex > lextable.h
*
* Run it twice to test parsing on the generated table on stderr
*
* Whoo this got a bit complicated by lws-buildtime deselection of some
* headers optionally. There are 3 x vars, UNCOMMON, WS, H2 so we make
* eight copies of the lextable selected by the appropriate #if defined()
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* get all the strings */
#define LWS_ROLE_WS 1
#define LWS_WITH_HTTP_UNCOMMON_HEADERS 1
#define LWS_ROLE_H2 1
#include "lextable-strings.h"
#undef LWS_ROLE_WS
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
#undef LWS_ROLE_H2
/* bitfield for the 8 versions as to which strings exist... index layout
*
* b0 b1 b2
* 0 =
* 1 = uncommon
* 2 = ws
* 3 = uncommon ws
* 4 = h2
* 5 = uncommon h2
* 6 = ws h2
* 7 = uncommon ws h2
*/
unsigned char filter_array[] = {
0xff, /* get */
0xff, /* post */
0xaa, /* options */
0xff, /* host */
0xff, /* connection */
0xff, /* upgrade */
0xff, /* origin */
0xcc, /* sec-ws-draft */
0xff, /* crlf */
0xcc, /* sec-ws-ext */
0xcc, /* sec-ws-key1 */
0xcc, /* sec-ws-key2 */
0xcc, /* sec-ws-protocol */
0xcc, /* sec-ws-accept */
0xcc, /* sec-ws-nonce */
0xff, /* http/1.1 */
0xf0, /* http2-settings */
0xff, /* accept */
0xaa, /* access-control-req-hdrs */
0xff, /* if-modified-since */
0xff, /* if-none-match */
0xff, /* accept-encoding */
0xff, /* accept-language */
0xff, /* pragma */
0xff, /* cache-control */
0xff, /* authorization */
0xff, /* cookie */
0xff, /* content-length */
0xff, /* content-type */
0xff, /* date */
0xff, /* range */
0xfa, /* referer */
0xcc, /* sec-ws-key */
0xcc, /* sec-ws-version */
0xcc, /* sec-sc-origin */
0xf0, /* authority */
0xf0, /* method */
0xf0, /* path */
0xf0, /* scheme */
0xf0, /* status */
0xfa, /* accept-charset */
0xff, /* accept-ranges */
0xfa, /* access-control-allow-origin */
0xff, /* age */
0xff, /* allow */
0xff, /* content-disposition */
0xff, /* content-encoding */
0xff, /* content-language */
0xff, /* content-location */
0xff, /* content-range */
0xff, /* etag */
0xff, /* expect */
0xff, /* expires */
0xff, /* from */
0xff, /* if-match */
0xff, /* if-range */
0xff, /* if-unmodified-since */
0xff, /* last-modified */
0xff, /* link */
0xff, /* location */
0xfa, /* max-forwards */
0xfa, /* proxy-authenticate */
0xfa, /* proxy-authorization */
0xff, /* refresh */
0xff, /* retry-after */
0xff, /* server */
0xff, /* set-cookie */
0xfa, /* strict-transport-security */
0xff, /* transfer-encoding */
0xfa, /* user-agent */
0xfa, /* vary */
0xfa, /* via */
0xfa, /* www-authenticate */
0xaa, /* patch */
0xaa, /* put */
0xaa, /* delete */
0xff, /* uri-args */
0xaa, /* proxy */
0xaa, /* x-real-ip */
0xff, /* http/1.0 */
0xff, /* x-forwarded-for */
0xff, /* connect */
0xff, /* head */
0xfa, /* te */
0xfa, /* replay-nonce */
0xf0, /* protocol */
0xff, /* x-auth-token */
0xff /* not matchable */
};
static unsigned char lws_header_implies_psuedoheader_map[] = {
0x07, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00 /* <-64 */,
0x0e /* <- 72 */, 0x24 /* <- 80 */, 0, 0, 0, 0
};
/*
* b7 = 0 = 1-byte seq
* 0x08 = fail
* 2-byte seq
* 0x00 - 0x07, then terminal as given in 2nd byte
3-byte seq
* no match: go fwd 3 byte, match: jump fwd by amt in +1/+2 bytes
* = 1 = 1-byte seq
* no match: die, match go fwd 1 byte
*/
unsigned char lextable[][2000] = {
{
#include "lextable.h"
},
#define LWS_WITH_HTTP_UNCOMMON_HEADERS
{
#include "lextable.h"
},
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
#define LWS_ROLE_WS 1
{
#include "lextable.h"
},
#define LWS_WITH_HTTP_UNCOMMON_HEADERS
{
#include "lextable.h"
},
#undef LWS_ROLE_WS
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
#define LWS_ROLE_H2 1
{
#include "lextable.h"
},
#define LWS_WITH_HTTP_UNCOMMON_HEADERS
{
#include "lextable.h"
},
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
#define LWS_ROLE_WS 1
{
#include "lextable.h"
},
#define LWS_WITH_HTTP_UNCOMMON_HEADERS 1
{
#include "lextable.h"
},
};
#define PARALLEL 30
struct state {
char c[PARALLEL];
int state[PARALLEL];
int count;
int bytepos;
int real_pos;
};
static unsigned char pseudomap[8][16];
struct state state[1000];
int next = 1;
#define FAIL_CHAR 0x08
int lextable_decode(int version, int pos, char c)
{
while (1) {
if (lextable[version][pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
if ((lextable[version][pos] & 0x7f) != c)
return -1;
/* fall thru */
pos++;
if (lextable[version][pos] == FAIL_CHAR)
return -1;
return pos;
} else { /* b7 = 0, end or 3-byte */
if (lextable[version][pos] < FAIL_CHAR) /* terminal marker */
return pos;
if (lextable[version][pos] == c) /* goto */
return pos + (lextable[version][pos + 1]) +
(lextable[version][pos + 2] << 8);
/* fall thru goto */
pos += 3;
/* continue */
}
}
}
int issue(int version)
{
const char *rset[200];
int n = 0;
int m;
int prev;
int walk;
int saw;
int y;
int j;
int pos = 0;
int setmembers = 0;
memset(rset, 0, sizeof(rset));
if (version == 7)
printf("#if defined(LWS_HTTP_HEADERS_ALL) || (%cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
"%cdefined(LWS_ROLE_WS) && "
"%cdefined(LWS_ROLE_H2))\n", version & 1 ? ' ' : '!',
version & 2 ? ' ' : '!', version & 4 ? ' ' : '!');
else
printf("#if !defined(LWS_HTTP_HEADERS_ALL) && %cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
"%cdefined(LWS_ROLE_WS) && "
"%cdefined(LWS_ROLE_H2)\n", version & 1 ? ' ' : '!',
version & 2 ? ' ' : '!', version & 4 ? ' ' : '!');
/*
* let's create version's view of the set of strings
*/
for (n = 0; n < sizeof(set) / sizeof(set[0]); n++)
if (filter_array[n] & (1 << version)) {
printf("\t/* %d: %d: %s */\n", setmembers, n, set[n]);
if (lws_header_implies_psuedoheader_map[n >> 3] & (1 << (n & 7)))
pseudomap[version][(setmembers >> 3)] |= 1 << (setmembers & 7);
rset[setmembers++] = set[n];
}
n = 0;
while (n < setmembers) {
m = 0;
walk = 0;
prev = 0;
if (rset[n][0] == '\0') {
n++;
continue;
}
while (rset[n][m]) {
saw = 0;
for (y = 0; y < state[walk].count; y++)
if (state[walk].c[y] == rset[n][m]) {
/* exists -- go forward */
walk = state[walk].state[y];
saw = 1;
break;
}
if (saw)
goto again;
/* something we didn't see before */
state[walk].c[state[walk].count] = rset[n][m];
state[walk].state[state[walk].count] = next;
state[walk].count++;
walk = next++;
again:
m++;
}
state[walk].c[0] = n++;
state[walk].state[0] = 0; /* terminal marker */
state[walk].count = 1;
}
walk = 0;
for (n = 0; n < next; n++) {
state[n].bytepos = walk;
walk += (2 * state[n].count);
}
/* compute everyone's position first */
pos = 0;
walk = 0;
for (n = 0; n < next; n++) {
state[n].real_pos = pos;
for (m = 0; m < state[n].count; m++) {
if (state[n].state[m] == 0)
pos += 2; /* terminal marker */
else { /* c is a character */
if ((state[state[n].state[m]].bytepos -
walk) == 2)
pos++;
else {
pos += 3;
if (m == state[n].count - 1)
pos++; /* fail */
}
}
walk += 2;
}
}
walk = 0;
pos = 0;
for (n = 0; n < next; n++) {
for (m = 0; m < state[n].count; m++) {
if (!m)
fprintf(stdout, "/* pos %04x: %3d */ ",
state[n].real_pos, n);
else
fprintf(stdout, " ");
y = state[n].c[m];
saw = state[n].state[m];
if (saw == 0) { // c is a terminal then
if (y > 0x7ff) {
fprintf(stderr, "terminal too big\n");
return 2;
}
fprintf(stdout, " 0x%02X, 0x%02X "
" "
"/* - terminal marker %2d - */,\n",
y >> 8, y & 0xff, y & 0x7f);
pos += 2;
walk += 2;
continue;
}
/* c is a character */
prev = y &0x7f;
if (prev < 32 || prev > 126)
prev = '.';
if ((state[saw].bytepos - walk) == 2) {
fprintf(stdout, " 0x%02X /* '%c' -> */,\n",
y | 0x80, prev);
pos++;
walk += 2;
continue;
}
j = state[saw].real_pos - pos;
if (j > 0xffff) {
fprintf(stderr,
"Jump > 64K bytes ahead (%d to %d)\n",
state[n].real_pos, state[saw].real_pos);
return 1;
}
fprintf(stdout, " 0x%02X /* '%c' */, 0x%02X, 0x%02X "
"/* (to 0x%04X state %3d) */,\n",
y, prev,
j & 0xff, j >> 8,
state[saw].real_pos, saw);
pos += 3;
if (m == state[n].count - 1) {
fprintf(stdout,
" 0x%02X, /* fail */\n",
FAIL_CHAR);
pos++; /* fail */
}
walk += 2;
}
}
fprintf(stdout, "/* total size %d bytes */\n", pos);
printf("#endif\n\n");
/*
* Try to parse every legal input string
*/
for (n = 0; n < setmembers; n++) {
walk = 0;
m = 0;
y = -1;
if (rset[n][0] == '\0')
continue;
fprintf(stderr, " trying %d '%s'\n", n, rset[n]);
while (rset[n][m]) {
walk = lextable_decode(version, walk, rset[n][m]);
if (walk < 0) {
fprintf(stderr, "failed\n");
return 3;
}
if (lextable[version][walk] < FAIL_CHAR) {
y = (lextable[version][walk] << 8) +
lextable[version][walk + 1];
break;
}
m++;
}
if (y != n) {
fprintf(stderr, "decode failed %d\n", y);
return 4;
}
}
fprintf(stderr, "All decode OK\n");
return 0;
}
int main(void)
{
int m, n;
for (n = 0; n < 8; n++) {
issue(n);
}
printf("\n/*\n");
for (n = 0; n < 8; n++) {
if (n == 7)
printf("#if defined(LWS_HTTP_HEADERS_ALL) || (%cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
"%cdefined(LWS_ROLE_WS) && "
"%cdefined(LWS_ROLE_H2))\n", n & 1 ? ' ' : '!',
n & 2 ? ' ' : '!', n & 4 ? ' ' : '!');
else
printf("#if !defined(LWS_HTTP_HEADERS_ALL) && %cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
"%cdefined(LWS_ROLE_WS) && "
"%cdefined(LWS_ROLE_H2)\n", n & 1 ? ' ' : '!',
n & 2 ? ' ' : '!', n & 4 ? ' ' : '!');
printf("static uint8_t lws_header_implies_psuedoheader_map[] = {\n\t");
for (m = 0; m < sizeof(pseudomap[n]); m++)
printf("0x%02x,", pseudomap[n][m]);
printf("\n};\n");
printf("#endif\n");
}
printf("*/\n");
fprintf(stderr, "did all the variants\n");
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,355 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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.
*
* This is included from private-lib-core.h if either H1 or H2 roles are
* enabled
*/
#if defined(LWS_WITH_HUBBUB)
#include <hubbub/hubbub.h>
#include <hubbub/parser.h>
#endif
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
#include "private-lib-roles-http-compression.h"
#endif
#define lwsi_role_http(wsi) (lwsi_role_h1(wsi) || lwsi_role_h2(wsi))
enum http_version {
HTTP_VERSION_1_0,
HTTP_VERSION_1_1,
HTTP_VERSION_2
};
enum http_conn_type {
HTTP_CONNECTION_CLOSE,
HTTP_CONNECTION_KEEP_ALIVE
};
/*
* This is totally opaque to code using the library. It's exported as a
* forward-reference pointer-only declaration; the user can use the pointer with
* other APIs to get information out of it.
*/
#if defined(LWS_PLAT_FREERTOS)
typedef uint16_t ah_data_idx_t;
#else
typedef uint32_t ah_data_idx_t;
#endif
struct lws_fragments {
ah_data_idx_t offset;
uint16_t len;
uint8_t nfrag; /* which ah->frag[] continues this content, or 0 */
uint8_t flags; /* only http2 cares */
};
#if defined(LWS_WITH_RANGES)
enum range_states {
LWSRS_NO_ACTIVE_RANGE,
LWSRS_BYTES_EQ,
LWSRS_FIRST,
LWSRS_STARTING,
LWSRS_ENDING,
LWSRS_COMPLETED,
LWSRS_SYNTAX,
};
struct lws_range_parsing {
unsigned long long start, end, extent, agg, budget;
const char buf[128];
int pos;
enum range_states state;
char start_valid, end_valid, ctr, count_ranges, did_try, inside, send_ctr;
};
int
lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
unsigned long long extent);
int
lws_ranges_next(struct lws_range_parsing *rp);
void
lws_ranges_reset(struct lws_range_parsing *rp);
#endif
#define LWS_HTTP_NO_KNOWN_HEADER 0xff
/*
* these are assigned from a pool held in the context.
* Both client and server mode uses them for http header analysis
*/
struct allocated_headers {
struct allocated_headers *next; /* linked list */
struct lws *wsi; /* owner */
char *data; /* prepared by context init to point to dedicated storage */
ah_data_idx_t data_length;
/*
* the randomly ordered fragments, indexed by frag_index and
* lws_fragments->nfrag for continuation.
*/
struct lws_fragments frags[WSI_TOKEN_COUNT];
time_t assigned;
/*
* for each recognized token, frag_index says which frag[] his data
* starts in (0 means the token did not appear)
* the actual header data gets dumped as it comes in, into data[]
*/
uint8_t frag_index[WSI_TOKEN_COUNT];
#if defined(LWS_WITH_CLIENT)
char initial_handshake_hash_base64[30];
#endif
int hdr_token_idx;
ah_data_idx_t pos;
ah_data_idx_t http_response;
ah_data_idx_t current_token_limit;
ah_data_idx_t unk_pos; /* to undo speculative unknown header */
#if defined(LWS_WITH_CUSTOM_HEADERS)
ah_data_idx_t unk_value_pos;
ah_data_idx_t unk_ll_head;
ah_data_idx_t unk_ll_tail;
#endif
int16_t lextable_pos;
uint8_t in_use;
uint8_t nfrag;
char /*enum uri_path_states */ ups;
char /*enum uri_esc_states */ ues;
char esc_stash;
char post_literal_equal;
uint8_t /* enum lws_token_indexes */ parser_state;
};
#if defined(LWS_WITH_HUBBUB)
struct lws_rewrite {
hubbub_parser *parser;
hubbub_parser_optparams params;
const char *from, *to;
int from_len, to_len;
unsigned char *p, *end;
struct lws *wsi;
};
static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len)
{
if ((int)s->len != len)
return 1;
return strncmp((const char *)s->ptr, p, len);
}
typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw);
LWS_EXTERN struct lws_rewrite *
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to);
LWS_EXTERN void
lws_rewrite_destroy(struct lws_rewrite *r);
LWS_EXTERN int
lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
#endif
struct lws_pt_role_http {
struct allocated_headers *ah_list;
struct lws *ah_wait_list;
#ifdef LWS_WITH_CGI
struct lws_cgi *cgi_list;
#endif
int ah_wait_list_length;
uint32_t ah_pool_length;
int ah_count_in_use;
};
struct lws_peer_role_http {
uint32_t count_ah;
uint32_t total_ah;
};
struct lws_vhost_role_http {
#if defined(LWS_CLIENT_HTTP_PROXYING)
char http_proxy_address[128];
#endif
const struct lws_http_mount *mount_list;
const char *error_document_404;
#if defined(LWS_CLIENT_HTTP_PROXYING)
unsigned int http_proxy_port;
#endif
};
#ifdef LWS_WITH_ACCESS_LOG
struct lws_access_log {
char *header_log;
char *user_agent;
char *referrer;
unsigned long sent;
int response;
};
#endif
#define LWS_HTTP_CHUNK_HDR_MAX_SIZE (6 + 2) /* 6 hex digits and then CRLF */
#define LWS_HTTP_CHUNK_TRL_MAX_SIZE (2 + 5) /* CRLF, then maybe 0 CRLF CRLF */
struct _lws_http_mode_related {
struct lws *new_wsi_list;
unsigned char *pending_return_headers;
size_t pending_return_headers_len;
size_t prh_content_length;
#if defined(LWS_WITH_HTTP_PROXY)
struct lws_rewrite *rw;
struct lws_buflist *buflist_post_body;
#endif
struct allocated_headers *ah;
struct lws *ah_wait_list;
unsigned long writeable_len;
#if defined(LWS_WITH_FILE_OPS)
lws_filepos_t filepos;
lws_filepos_t filelen;
lws_fop_fd_t fop_fd;
#endif
#if defined(LWS_WITH_CLIENT)
char multipart_boundary[16];
#endif
#if defined(LWS_WITH_RANGES)
struct lws_range_parsing range;
char multipart_content_type[64];
#endif
#ifdef LWS_WITH_ACCESS_LOG
struct lws_access_log access_log;
#endif
#if defined(LWS_WITH_SERVER)
unsigned int response_code;
#endif
#ifdef LWS_WITH_CGI
struct lws_cgi *cgi; /* wsi being cgi stream have one of these */
#endif
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
struct lws_compression_support *lcs;
lws_comp_ctx_t comp_ctx;
unsigned char comp_accept_mask;
#endif
enum http_version request_version;
enum http_conn_type conn_type;
lws_filepos_t tx_content_length;
lws_filepos_t tx_content_remain;
lws_filepos_t rx_content_length;
lws_filepos_t rx_content_remain;
#if defined(LWS_WITH_HTTP_PROXY)
unsigned int perform_rewrite:1;
unsigned int proxy_clientside:1;
unsigned int proxy_parent_chunked:1;
#endif
unsigned int deferred_transaction_completed:1;
unsigned int content_length_explicitly_zero:1;
unsigned int content_length_given:1;
unsigned int did_stream_close:1;
unsigned int multipart:1;
unsigned int cgi_transaction_complete:1;
unsigned int multipart_issue_boundary:1;
char auth_username[64];
char auth_password[64];
char *digest_auth_hdr;
};
#if defined(LWS_WITH_CLIENT)
enum lws_chunk_parser {
ELCP_HEX,
ELCP_CR,
ELCP_CONTENT,
ELCP_POST_CR,
ELCP_POST_LF,
ELCP_TRAILER_CR,
ELCP_TRAILER_LF
};
#endif
enum lws_parse_urldecode_results {
LPUR_CONTINUE,
LPUR_SWALLOW,
LPUR_FORBID,
LPUR_EXCESSIVE,
};
enum lws_check_basic_auth_results {
LCBA_CONTINUE,
LCBA_FAILED_AUTH,
LCBA_END_TRANSACTION,
};
enum lws_check_basic_auth_results
lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file, unsigned int auth_mode);
int
lws_unauthorised_basic_auth(struct lws *wsi);
int
lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
void
_lws_header_table_reset(struct allocated_headers *ah);
LWS_EXTERN int
_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah);
int
lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
char *uri_ptr, char ws);
void
lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t *sul);
uint8_t *
lws_http_multipart_headers(struct lws *wsi, uint8_t *p);
int
lws_http_string_to_known_header(const char *s, size_t slen);
int
lws_http_date_render_from_unix(char *buf, size_t len, const time_t *t);
int
lws_http_date_parse_unix(const char *b, size_t len, time_t *t);
enum {
CCTLS_RETURN_ERROR = -1,
CCTLS_RETURN_DONE = 0,
CCTLS_RETURN_RETRY = 1,
};
int
lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1);

View File

@ -0,0 +1,209 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
/*
* Produce Apache-compatible log string for wsi, like this:
*
* 2.31.234.19 - - [27/Mar/2016:03:22:44 +0800]
* "GET /aep-screen.png HTTP/1.1"
* 200 152987 "https://libwebsockets.org/index.html"
* "Mozilla/5.0 (Macint... Chrome/49.0.2623.87 Safari/537.36"
*
*/
extern const char * const method_names[];
static const char * const hver[] = {
"HTTP/1.0", "HTTP/1.1", "HTTP/2"
};
void
lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int uri_len, int meth)
{
char da[64], uri[256], ta[64];
time_t t = time(NULL);
struct lws *nwsi;
const char *me;
int l = 256, m;
struct tm *ptm = NULL;
#if defined(LWS_HAVE_LOCALTIME_R)
struct tm tm;
#endif
if (!wsi->a.vhost)
return;
/* only worry about preparing it if we store it */
if (wsi->a.vhost->log_fd == (int)LWS_INVALID_FILE)
return;
if (wsi->access_log_pending)
lws_access_log(wsi);
wsi->http.access_log.header_log = lws_malloc((unsigned int)l, "access log");
if (!wsi->http.access_log.header_log)
return;
#if defined(LWS_HAVE_LOCALTIME_R)
ptm = localtime_r(&t, &tm);
#else
ptm = localtime(&t);
#endif
if (ptm)
strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", ptm);
else
strcpy(da, "01/Jan/1970:00:00:00 +0000");
#if defined(LWS_ROLE_H2)
if (wsi->mux_substream)
me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
else
#endif
me = method_names[meth];
if (!me)
me = "(null)";
m = uri_len;
if (m > (int)sizeof(uri) - 1)
m = sizeof(uri) - 1;
strncpy(uri, uri_ptr, (unsigned int)m);
uri[m] = '\0';
nwsi = lws_get_network_wsi(wsi);
if (nwsi->sa46_peer.sa4.sin_family)
lws_sa46_write_numeric_address(&nwsi->sa46_peer, ta, sizeof(ta));
else
strncpy(ta, "unknown", sizeof(ta));
lws_snprintf(wsi->http.access_log.header_log, (size_t)l,
"%s - - [%s] \"%s %s %s\"",
ta, da, me, uri, hver[wsi->http.request_version]);
//lwsl_notice("%s\n", wsi->http.access_log.header_log);
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
if (l) {
wsi->http.access_log.user_agent =
lws_malloc((unsigned int)l + 5, "access log");
if (!wsi->http.access_log.user_agent) {
lwsl_err("OOM getting user agent\n");
lws_free_set_NULL(wsi->http.access_log.header_log);
return;
}
wsi->http.access_log.user_agent[0] = '\0';
if (lws_hdr_copy(wsi, wsi->http.access_log.user_agent, l + 4,
WSI_TOKEN_HTTP_USER_AGENT) >= 0)
for (m = 0; m < l; m++)
if (wsi->http.access_log.user_agent[m] == '\"')
wsi->http.access_log.user_agent[m] = '\'';
}
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER);
if (l) {
wsi->http.access_log.referrer = lws_malloc((unsigned int)l + 5, "referrer");
if (!wsi->http.access_log.referrer) {
lwsl_err("OOM getting referrer\n");
lws_free_set_NULL(wsi->http.access_log.user_agent);
lws_free_set_NULL(wsi->http.access_log.header_log);
return;
}
wsi->http.access_log.referrer[0] = '\0';
if (lws_hdr_copy(wsi, wsi->http.access_log.referrer,
l + 4, WSI_TOKEN_HTTP_REFERER) >= 0)
for (m = 0; m < l; m++)
if (wsi->http.access_log.referrer[m] == '\"')
wsi->http.access_log.referrer[m] = '\'';
}
wsi->access_log_pending = 1;
}
int
lws_access_log(struct lws *wsi)
{
char *p = wsi->http.access_log.user_agent, ass[512],
*p1 = wsi->http.access_log.referrer;
int l;
if (!wsi->a.vhost)
return 0;
if (wsi->a.vhost->log_fd == (int)LWS_INVALID_FILE)
return 0;
if (!wsi->access_log_pending)
return 0;
if (!wsi->http.access_log.header_log)
return 0;
if (!p)
p = "";
if (!p1)
p1 = "";
/*
* We do this in two parts to restrict an oversize referrer such that
* we will always have space left to append an empty useragent, while
* maintaining the structure of the log text
*/
l = lws_snprintf(ass, sizeof(ass) - 7, "%s %d %lu \"%s",
wsi->http.access_log.header_log,
wsi->http.access_log.response,
wsi->http.access_log.sent, p1);
if (strlen(p) > sizeof(ass) - 6 - (unsigned int)l) {
p[sizeof(ass) - 6 - (unsigned int)l] = '\0';
l--;
}
l += lws_snprintf(ass + (unsigned int)l, sizeof(ass) - 1 - (unsigned int)l, "\" \"%s\"\n", p);
ass[sizeof(ass) - 1] = '\0';
if ((int)write(wsi->a.vhost->log_fd, ass, (size_t)l) != l)
lwsl_err("Failed to write log\n");
if (wsi->http.access_log.header_log) {
lws_free(wsi->http.access_log.header_log);
wsi->http.access_log.header_log = NULL;
}
if (wsi->http.access_log.user_agent) {
lws_free(wsi->http.access_log.user_agent);
wsi->http.access_log.user_agent = NULL;
}
if (wsi->http.access_log.referrer) {
lws_free(wsi->http.access_log.referrer);
wsi->http.access_log.referrer = NULL;
}
wsi->access_log_pending = 0;
return 0;
}

View File

@ -0,0 +1,657 @@
/*
* 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.
*
* Somewhat rewritten by AG
*/
#include "private-lib-core.h"
#if defined(LWS_WITH_MINIZ)
#include <miniz.h>
#else
#include <zlib.h>
#endif
/*
* 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,
};
#define eff_size(_priv) (_priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE ? \
_priv->hdr.uncomp_size : _priv->hdr.comp_size)
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 >= (int)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, (unsigned int)len))
return LWS_FZ_ERR_NAME_READ;
if ((int)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,
(lws_fileofs_t)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,
(lws_fileofs_t)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, (lws_fileofs_t)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_own,
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 = lws_ptr_diff(vpath, vfs_path) - 1;
lws_strncpy(rp, vfs_path, (unsigned int)m + 1);
/* open the zip file itself using the incoming fops, not fops_zip */
priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops_own, fops, rp, NULL, &local_flags);
if (!priv->zip_fop_fd) {
lwsl_err("%s: unable to open zip %s\n", __func__, rp);
goto bail1;
}
if (*vpath == '/')
vpath++;
m = lws_fops_zip_scan(priv, vpath, (int)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 = (lws_filepos_t)((lws_fileofs_t)fd->pos + offset_from_cur_pos);
return (lws_fileofs_t)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 > eff_size(priv) - (cur - priv->content_start))
rlen = eff_size(priv) - (cur - 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;
/* fallthru */
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 > eff_size(priv) - cur)
len = eff_size(priv) - cur;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
amount, buf, len))
return LWS_FZ_ERR_READ_CONTENT;
fd->pos += *amount;
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,
NULL,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,725 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#define LWS_MAX_ELEM_NAME 32
enum urldecode_stateful {
US_NAME,
US_IDLE,
US_PC1,
US_PC2,
MT_LOOK_BOUND_IN,
MT_HNAME,
MT_DISP,
MT_TYPE,
MT_IGNORE1,
MT_IGNORE2,
MT_IGNORE3,
MT_COMPLETED,
};
static struct mp_hdr {
const char * const hdr;
uint8_t hdr_len;
} mp_hdrs[] = {
{ "content-disposition: ", 21 },
{ "content-type: ", 14 },
{ "\x0d\x0a", 2 }
};
struct lws_spa;
typedef int (*lws_urldecode_stateful_cb)(struct lws_spa *spa,
const char *name, char **buf, int len, int final);
struct lws_urldecode_stateful {
char *out;
struct lws_spa *data;
struct lws *wsi;
char name[LWS_MAX_ELEM_NAME];
char temp[LWS_MAX_ELEM_NAME];
char content_type[32];
char content_disp[32];
char content_disp_filename[256];
char mime_boundary[128];
int out_len;
int pos;
int hdr_idx;
int mp;
int sum;
uint8_t matchable;
uint8_t multipart_form_data:1;
uint8_t inside_quote:1;
uint8_t subname:1;
uint8_t boundary_real_crlf:1;
enum urldecode_stateful state;
lws_urldecode_stateful_cb output;
};
struct lws_spa {
struct lws_urldecode_stateful *s;
lws_spa_create_info_t i;
int *param_length;
char finalized;
char **params;
char *storage;
char *end;
};
static struct lws_urldecode_stateful *
lws_urldecode_s_create(struct lws_spa *spa, struct lws *wsi, char *out,
int out_len, lws_urldecode_stateful_cb output)
{
struct lws_urldecode_stateful *s;
char buf[205], *p;
int m = 0;
if (spa->i.ac)
s = lwsac_use_zero(spa->i.ac, sizeof(*s), spa->i.ac_chunk_size);
else
s = lws_zalloc(sizeof(*s), "stateful urldecode");
if (!s)
return NULL;
s->out = out;
s->out_len = out_len;
s->output = output;
s->pos = 0;
s->sum = 0;
s->mp = 0;
s->state = US_NAME;
s->name[0] = '\0';
s->data = spa;
s->wsi = wsi;
if (lws_hdr_copy(wsi, buf, sizeof(buf),
WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
/* multipart/form-data;
* boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
if (!strncmp(buf, "multipart/form-data", 19) ||
!strncmp(buf, "multipart/related", 17)) {
s->multipart_form_data = 1;
s->state = MT_LOOK_BOUND_IN;
s->mp = 2;
p = strstr(buf, "boundary=");
if (p) {
p += 9;
s->mime_boundary[m++] = '\x0d';
s->mime_boundary[m++] = '\x0a';
s->mime_boundary[m++] = '-';
s->mime_boundary[m++] = '-';
if (*p == '\"')
p++;
while (m < (int)sizeof(s->mime_boundary) - 1 &&
*p && *p != ' ' && *p != ';' && *p != '\"')
s->mime_boundary[m++] = *p++;
s->mime_boundary[m] = '\0';
// lwsl_notice("boundary '%s'\n", s->mime_boundary);
}
}
}
return s;
}
static int
lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in,
int len)
{
int n, hit;
char c;
while (len--) {
if (s->pos == s->out_len - s->mp - 1) {
if (s->output(s->data, s->name, &s->out, s->pos,
LWS_UFS_CONTENT))
return -1;
s->pos = 0;
}
switch (s->state) {
/* states for url arg style */
case US_NAME:
s->inside_quote = 0;
if (*in == '=') {
s->name[s->pos] = '\0';
s->pos = 0;
s->state = US_IDLE;
in++;
continue;
}
if (*in == '&') {
s->name[s->pos] = '\0';
if (s->output(s->data, s->name, &s->out,
s->pos, LWS_UFS_FINAL_CONTENT))
return -1;
s->pos = 0;
s->state = US_IDLE;
in++;
continue;
}
if (s->pos >= (int)sizeof(s->name) - 1) {
lwsl_hexdump_notice(s->name, (size_t)s->pos);
lwsl_notice("Name too long...\n");
return -1;
}
s->name[s->pos++] = *in++;
break;
case US_IDLE:
if (*in == '%') {
s->state++;
in++;
continue;
}
if (*in == '&') {
s->out[s->pos] = '\0';
if (s->output(s->data, s->name, &s->out,
s->pos, LWS_UFS_FINAL_CONTENT))
return -1;
s->pos = 0;
s->state = US_NAME;
in++;
continue;
}
if (*in == '+') {
in++;
s->out[s->pos++] = ' ';
continue;
}
s->out[s->pos++] = *in++;
break;
case US_PC1:
n = char_to_hex(*in);
if (n < 0)
return -1;
in++;
s->sum = n << 4;
s->state++;
break;
case US_PC2:
n = char_to_hex(*in);
if (n < 0)
return -1;
in++;
s->out[s->pos++] = (char)(s->sum | n);
s->state = US_IDLE;
break;
/* states for multipart / mime style */
case MT_LOOK_BOUND_IN:
retry_as_first:
if (*in == s->mime_boundary[s->mp] &&
s->mime_boundary[s->mp]) {
in++;
s->mp++;
if (!s->mime_boundary[s->mp]) {
s->mp = 0;
s->state = MT_IGNORE1;
if (s->output(s->data, s->name,
&s->out, s->pos,
LWS_UFS_FINAL_CONTENT))
return -1;
s->pos = 0;
s->content_disp[0] = '\0';
s->name[0] = '\0';
s->content_disp_filename[0] = '\0';
s->boundary_real_crlf = 1;
}
continue;
}
if (s->mp) {
n = 0;
if (!s->boundary_real_crlf)
n = 2;
if (s->mp >= n) {
memcpy(s->out + s->pos,
s->mime_boundary + n,
(unsigned int)(s->mp - n));
s->pos += s->mp;
s->mp = 0;
goto retry_as_first;
}
}
s->out[s->pos++] = *in;
in++;
s->mp = 0;
break;
case MT_HNAME:
c =*in;
if (c >= 'A' && c <= 'Z')
c = (char)(c + 'a' - 'A');
if (!s->mp)
/* initially, any of them might match */
s->matchable = (1 << LWS_ARRAY_SIZE(mp_hdrs)) - 1;
hit = -1;
for (n = 0; n < (int)LWS_ARRAY_SIZE(mp_hdrs); n++) {
if (!(s->matchable & (1 << n)))
continue;
/* this guy is still in contention... */
if (s->mp >= mp_hdrs[n].hdr_len) {
/* he went past the end of it */
s->matchable &= (uint8_t)~(1 << n);
continue;
}
if (c != mp_hdrs[n].hdr[s->mp]) {
/* mismatched a char */
s->matchable &= (uint8_t)~(1 << n);
continue;
}
if (s->mp + 1 == mp_hdrs[n].hdr_len) {
/* we have a winner... */
hit = n;
break;
}
}
in++;
if (hit == -1 && !s->matchable) {
/* We ruled them all out */
s->state = MT_IGNORE1;
s->mp = 0;
continue;
}
s->mp++;
if (hit < 0)
continue;
/* we matched the one in hit */
s->mp = 0;
s->temp[0] = '\0';
s->subname = 0;
if (hit == 2)
s->state = MT_LOOK_BOUND_IN;
else
s->state += (unsigned int)hit + 1u;
break;
case MT_DISP:
/* form-data; name="file"; filename="t.txt" */
if (*in == '\x0d') {
if (s->content_disp_filename[0])
if (s->output(s->data, s->name,
&s->out, s->pos,
LWS_UFS_OPEN))
return -1;
s->state = MT_IGNORE2;
goto done;
}
if (*in == ';') {
s->subname = 1;
s->temp[0] = '\0';
s->mp = 0;
goto done;
}
if (*in == '\"') {
s->inside_quote = !!((s->inside_quote ^ 1) & 1);
goto done;
}
if (s->subname) {
if (*in == '=') {
s->temp[s->mp] = '\0';
s->subname = 0;
s->mp = 0;
goto done;
}
if (s->mp < (int)sizeof(s->temp) - 1 &&
(*in != ' ' || s->inside_quote))
s->temp[s->mp++] = *in;
goto done;
}
if (!s->temp[0]) {
if (s->mp < (int)sizeof(s->content_disp) - 1)
s->content_disp[s->mp++] = *in;
if (s->mp < (int)sizeof(s->content_disp))
s->content_disp[s->mp] = '\0';
goto done;
}
if (!strcmp(s->temp, "name")) {
if (s->mp < (int)sizeof(s->name) - 1)
s->name[s->mp++] = *in;
else
s->mp = (int)sizeof(s->name) - 1;
s->name[s->mp] = '\0';
goto done;
}
if (!strcmp(s->temp, "filename")) {
if (s->mp < (int)sizeof(s->content_disp_filename) - 1)
s->content_disp_filename[s->mp++] = *in;
s->content_disp_filename[s->mp] = '\0';
goto done;
}
done:
in++;
break;
case MT_TYPE:
if (*in == '\x0d')
s->state = MT_IGNORE2;
else {
if (s->mp < (int)sizeof(s->content_type) - 1)
s->content_type[s->mp++] = *in;
s->content_type[s->mp] = '\0';
}
in++;
break;
case MT_IGNORE1:
if (*in == '\x0d')
s->state = MT_IGNORE2;
if (*in == '-')
s->state = MT_IGNORE3;
in++;
break;
case MT_IGNORE2:
s->mp = 0;
if (*in == '\x0a')
s->state = MT_HNAME;
in++;
break;
case MT_IGNORE3:
if (*in == '\x0d')
s->state = MT_IGNORE2;
if (*in == '-') {
s->state = MT_COMPLETED;
s->wsi->http.rx_content_remain = 0;
}
in++;
break;
case MT_COMPLETED:
break;
}
}
return 0;
}
static int
lws_urldecode_s_destroy(struct lws_spa *spa, struct lws_urldecode_stateful *s)
{
int ret = 0;
if (s->state != US_IDLE)
ret = -1;
if (!ret)
if (s->output(s->data, s->name, &s->out, s->pos,
LWS_UFS_FINAL_CONTENT))
ret = -1;
if (s->output(s->data, s->name, NULL, 0, LWS_UFS_CLOSE))
return -1;
if (!spa->i.ac)
lws_free(s);
return ret;
}
static int
lws_urldecode_spa_lookup(struct lws_spa *spa, const char *name)
{
const char * const *pp = spa->i.param_names;
int n;
for (n = 0; n < spa->i.count_params; n++) {
if (!*pp && !spa->i.param_names_stride && spa->i.ac) {
unsigned int len = (unsigned int)strlen(name);
char **ptr = (char**)spa->i.param_names;
/* Use NULLs at end of list to dynamically create
* unknown entries */
ptr[n] = lwsac_use(spa->i.ac, len + 1, spa->i.ac_chunk_size);
if (!ptr[n])
return -1;
memcpy(ptr[n], name, len);
ptr[n][len] = '\0';
return n;
}
if (*pp && !strcmp(*pp, name))
return n;
if (spa->i.param_names_stride)
pp = (const char * const *)(((char *)pp) + spa->i.param_names_stride);
else
pp++;
}
return -1;
}
static int
lws_urldecode_spa_cb(struct lws_spa *spa, const char *name, char **buf, int len,
int final)
{
int n;
if (final == LWS_UFS_CLOSE || spa->s->content_disp_filename[0]) {
if (spa->i.opt_cb) {
n = spa->i.opt_cb(spa->i.opt_data, name,
spa->s->content_disp_filename,
buf ? *buf : NULL, len, (enum lws_spa_fileupload_states)final);
if (n < 0)
return -1;
}
return 0;
}
n = lws_urldecode_spa_lookup(spa, name);
if (n == -1 || !len) /* unrecognized */
return 0;
if (!spa->i.ac) {
if (!spa->params[n])
spa->params[n] = *buf;
if ((*buf) + len >= spa->end) {
lwsl_info("%s: exceeded storage\n", __func__);
return -1;
}
/* move it on inside storage */
(*buf) += len;
*((*buf)++) = '\0';
spa->s->out_len -= len + 1;
} else {
spa->params[n] = lwsac_use(spa->i.ac, (unsigned int)len + 1,
spa->i.ac_chunk_size);
if (!spa->params[n])
return -1;
memcpy(spa->params[n], *buf, (unsigned int)len);
spa->params[n][len] = '\0';
}
spa->param_length[n] += len;
return 0;
}
struct lws_spa *
lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *i)
{
struct lws_spa *spa;
if (i->ac)
spa = lwsac_use_zero(i->ac, sizeof(*spa), i->ac_chunk_size);
else
spa = lws_zalloc(sizeof(*spa), "spa");
if (!spa)
return NULL;
spa->i = *i;
if (!spa->i.max_storage)
spa->i.max_storage = 512;
if (i->ac)
spa->storage = lwsac_use(i->ac, (unsigned int)spa->i.max_storage,
i->ac_chunk_size);
else
spa->storage = lws_malloc((unsigned int)spa->i.max_storage, "spa");
if (!spa->storage)
goto bail2;
spa->end = spa->storage + spa->i.max_storage - 1;
if (i->count_params) {
if (i->ac)
spa->params = lwsac_use_zero(i->ac,
sizeof(char *) * (unsigned int)i->count_params, i->ac_chunk_size);
else
spa->params = lws_zalloc(sizeof(char *) * (unsigned int)i->count_params,
"spa params");
if (!spa->params)
goto bail3;
}
spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, spa->i.max_storage,
lws_urldecode_spa_cb);
if (!spa->s)
goto bail4;
if (i->count_params) {
if (i->ac)
spa->param_length = lwsac_use_zero(i->ac,
sizeof(int) * (unsigned int)i->count_params, i->ac_chunk_size);
else
spa->param_length = lws_zalloc(sizeof(int) * (unsigned int)i->count_params,
"spa param len");
if (!spa->param_length)
goto bail5;
}
// lwsl_notice("%s: Created SPA %p\n", __func__, spa);
return spa;
bail5:
lws_urldecode_s_destroy(spa, spa->s);
bail4:
if (!i->ac)
lws_free(spa->params);
bail3:
if (!i->ac)
lws_free(spa->storage);
bail2:
if (!i->ac)
lws_free(spa);
if (i->ac)
lwsac_free(i->ac);
return NULL;
}
struct lws_spa *
lws_spa_create(struct lws *wsi, const char * const *param_names,
int count_params, int max_storage,
lws_spa_fileupload_cb opt_cb, void *opt_data)
{
lws_spa_create_info_t i;
memset(&i, 0, sizeof(i));
i.count_params = count_params;
i.max_storage = max_storage;
i.opt_cb = opt_cb;
i.opt_data = opt_data;
i.param_names = param_names;
return lws_spa_create_via_info(wsi, &i);
}
int
lws_spa_process(struct lws_spa *spa, const char *in, int len)
{
if (!spa) {
lwsl_err("%s: NULL spa\n", __func__);
return -1;
}
/* we reject any junk after the last part arrived and we finalized */
if (spa->finalized)
return 0;
return lws_urldecode_s_process(spa->s, in, len);
}
int
lws_spa_get_length(struct lws_spa *spa, int n)
{
if (n >= spa->i.count_params)
return 0;
return spa->param_length[n];
}
const char *
lws_spa_get_string(struct lws_spa *spa, int n)
{
if (n >= spa->i.count_params)
return NULL;
return spa->params[n];
}
int
lws_spa_finalize(struct lws_spa *spa)
{
if (!spa)
return 0;
if (spa->s) {
lws_urldecode_s_destroy(spa, spa->s);
spa->s = NULL;
}
spa->finalized = 1;
return 0;
}
int
lws_spa_destroy(struct lws_spa *spa)
{
int n = 0;
lwsl_info("%s: destroy spa %p\n", __func__, spa);
if (spa->s)
lws_urldecode_s_destroy(spa, spa->s);
if (spa->i.ac)
lwsac_free(spa->i.ac);
else {
lws_free(spa->param_length);
lws_free(spa->params);
lws_free(spa->storage);
lws_free(spa);
}
return n;
}

View File

@ -0,0 +1,214 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.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=";
while (1) {
char 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 = (unsigned long long)(((unsigned long long)rp->start * 10) + (unsigned long long)(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 = (unsigned long long)(((unsigned long long)rp->end * 10) + (unsigned long long)(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;
}

View File

@ -0,0 +1,55 @@
#include "private-lib-core.h"
#if defined(LWS_WITH_HUBBUB)
struct lws_rewrite *
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from,
const char *to)
{
struct lws_rewrite *r = lws_malloc(sizeof(*r), "rewrite");
if (!r) {
lwsl_err("OOM\n");
return NULL;
}
if (hubbub_parser_create("UTF-8", false, &r->parser) != HUBBUB_OK) {
lws_free(r);
return NULL;
}
r->from = from;
r->from_len = strlen(from);
r->to = to;
r->to_len = strlen(to);
r->params.token_handler.handler = cb;
r->wsi = wsi;
r->params.token_handler.pw = (void *)r;
if (hubbub_parser_setopt(r->parser, HUBBUB_PARSER_TOKEN_HANDLER,
&r->params) != HUBBUB_OK) {
lws_free(r);
return NULL;
}
return r;
}
int
lws_rewrite_parse(struct lws_rewrite *r,
const unsigned char *in, int in_len)
{
if (r && hubbub_parser_parse_chunk(r->parser, in, in_len) != HUBBUB_OK)
return -1;
return 0;
}
void
lws_rewrite_destroy(struct lws_rewrite *r)
{
hubbub_parser_destroy(r->parser);
lws_free(r);
}
#endif

File diff suppressed because it is too large Load Diff