2011-03-07 17:54:06 +00:00
|
|
|
/*
|
2010-11-08 17:12:19 +00:00
|
|
|
* libwebsockets - small server side websockets and web server implementation
|
2010-12-19 22:13:26 +00:00
|
|
|
*
|
2019-01-13 06:58:21 +08:00
|
|
|
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
|
2010-11-08 17:12:19 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation:
|
|
|
|
* version 2.1 of the License.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
* MA 02110-1301 USA
|
2010-10-31 17:51:39 +00:00
|
|
|
*/
|
|
|
|
|
2018-05-03 10:49:36 +08:00
|
|
|
#include "core/private.h"
|
2016-02-29 18:48:55 +08:00
|
|
|
|
|
|
|
#ifdef LWS_HAVE_SYS_TYPES_H
|
2016-02-21 21:25:48 +08:00
|
|
|
#include <sys/types.h>
|
2016-02-29 18:48:55 +08:00
|
|
|
#endif
|
|
|
|
|
2018-04-27 19:16:50 +08:00
|
|
|
signed char char_to_hex(const char c)
|
|
|
|
{
|
|
|
|
if (c >= '0' && c <= '9')
|
|
|
|
return c - '0';
|
|
|
|
|
|
|
|
if (c >= 'a' && c <= 'f')
|
|
|
|
return c - 'a' + 10;
|
|
|
|
|
|
|
|
if (c >= 'A' && c <= 'F')
|
|
|
|
return c - 'A' + 10;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2019-01-15 06:59:48 +08:00
|
|
|
#if !defined(LWS_PLAT_OPTEE)
|
2018-04-27 19:16:50 +08:00
|
|
|
|
2018-06-23 12:56:21 +08:00
|
|
|
int lws_open(const char *__file, int __oflag, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
va_start(ap, __oflag);
|
|
|
|
if (((__oflag & O_CREAT) == O_CREAT)
|
|
|
|
#if defined(O_TMPFILE)
|
|
|
|
|| ((__oflag & O_TMPFILE) == O_TMPFILE)
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
/* last arg is really a mode_t. But windows... */
|
|
|
|
n = open(__file, __oflag, va_arg(ap, uint32_t));
|
|
|
|
else
|
|
|
|
n = open(__file, __oflag);
|
|
|
|
va_end(ap);
|
|
|
|
|
2018-08-01 06:52:03 +08:00
|
|
|
if (n != -1 && lws_plat_apply_FD_CLOEXEC(n)) {
|
|
|
|
close(n);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2018-06-23 12:56:21 +08:00
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
2019-01-15 06:59:48 +08:00
|
|
|
#endif
|
vhost_destroy: use vhost wsi reference counting to trigger destroy
This changes the vhost destroy flow to only hand off the listen
socket if another vhost sharing it, and mark the vhost as
being_destroyed.
Each tsi calls lws_check_deferred_free() once a second, if it sees
any vhost being_destroyed there, it closes all wsi on its tsi on
the same vhost, one time.
As the wsi on the vhost complete close (ie, after libuv async close
if on libuv event loop), they decrement a reference count for all
wsi open on the vhost. The tsi who closes the last one then
completes the destroy flow for the vhost itself... it's random
which tsi completes the vhost destroy but since there are no
wsi left on the vhost, and it holds the context lock, nothing
can conflict.
The advantage of this is that owning tsi do the close for wsi
that are bound to the vhost under destruction, at a time when
they are guaranteed to be idle for service, and they do it with
both vhost and context locks owned, so no other service thread
can conflict for stuff protected by those either.
For the situation the user code may have allocations attached to
the vhost, this adds args to lws_vhost_destroy() to allow destroying
the user allocations just before the vhost is freed.
2018-06-16 09:31:07 +08:00
|
|
|
|
2015-06-25 17:51:07 +02:00
|
|
|
|
2018-03-19 16:37:37 +08:00
|
|
|
void
|
2019-03-20 07:39:55 +08:00
|
|
|
lws_dll_add_head(struct lws_dll *d, struct lws_dll *phead)
|
2017-12-05 20:02:29 +08:00
|
|
|
{
|
2019-03-20 07:39:55 +08:00
|
|
|
if (!lws_dll_is_detached(d, phead)) {
|
|
|
|
assert(0); /* only wholly detached things can be added */
|
2018-03-19 16:37:37 +08:00
|
|
|
return;
|
2019-03-20 07:39:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* our next guy is current first guy, if any */
|
|
|
|
if (phead->next != d)
|
|
|
|
d->next = phead->next;
|
2018-03-19 16:37:37 +08:00
|
|
|
|
|
|
|
/* if there is a next guy, set his prev ptr to our next ptr */
|
|
|
|
if (d->next)
|
|
|
|
d->next->prev = d;
|
2019-03-20 07:39:55 +08:00
|
|
|
/* there is nobody previous to us, we are the head */
|
2019-03-19 11:54:27 +08:00
|
|
|
d->prev = NULL;
|
2019-03-20 07:39:55 +08:00
|
|
|
|
2018-03-19 16:37:37 +08:00
|
|
|
/* set the first guy to be us */
|
|
|
|
phead->next = d;
|
2019-03-19 11:54:27 +08:00
|
|
|
|
2019-03-20 07:39:55 +08:00
|
|
|
/* if there was nothing on the list before, we are also now the tail */
|
2019-03-19 11:54:27 +08:00
|
|
|
if (!phead->prev)
|
|
|
|
phead->prev = d;
|
2019-03-20 07:39:55 +08:00
|
|
|
|
|
|
|
assert(d->prev != d);
|
|
|
|
assert(d->next != d);
|
2019-03-19 11:54:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_dll_add_tail(struct lws_dll *d, struct lws_dll *phead)
|
|
|
|
{
|
2019-03-20 07:39:55 +08:00
|
|
|
if (!lws_dll_is_detached(d, phead)) {
|
|
|
|
assert(0); /* only wholly detached things can be added */
|
2019-03-19 11:54:27 +08:00
|
|
|
return;
|
2019-03-20 07:39:55 +08:00
|
|
|
}
|
2019-03-19 11:54:27 +08:00
|
|
|
|
|
|
|
/* our previous guy is current last guy */
|
|
|
|
d->prev = phead->prev;
|
|
|
|
/* if there is a prev guy, set his next ptr to our prev ptr */
|
|
|
|
if (d->prev)
|
|
|
|
d->prev->next = d;
|
|
|
|
/* our next ptr is NULL */
|
|
|
|
d->next = NULL;
|
|
|
|
/* set the last guy to be us */
|
|
|
|
phead->prev = d;
|
|
|
|
|
2019-03-20 07:39:55 +08:00
|
|
|
/* list head is also us if we're the first */
|
|
|
|
if (!phead->next)
|
|
|
|
phead->next = d;
|
|
|
|
|
|
|
|
assert(d->prev != d);
|
|
|
|
assert(d->next != d);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_dll_insert(struct lws_dll *n, struct lws_dll *target,
|
|
|
|
struct lws_dll *phead, int before)
|
|
|
|
{
|
|
|
|
if (!lws_dll_is_detached(n, phead)) {
|
|
|
|
assert(0); /* only wholly detached things can be inserted */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!target) {
|
|
|
|
/*
|
|
|
|
* the case where there's no target identified degenerates to
|
|
|
|
* a simple add at head or tail
|
|
|
|
*/
|
|
|
|
if (before) {
|
|
|
|
lws_dll_add_head(n, phead);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
lws_dll_add_tail(n, phead);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* in the case there's a target "cursor", we have to do the work to
|
|
|
|
* stitch the new guy in appropriately
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (before) {
|
|
|
|
/*
|
|
|
|
* we go before dd
|
|
|
|
* DDp <-> DD <-> DDn --> DDp <-> us <-> DD <-> DDn
|
|
|
|
*/
|
|
|
|
/* we point forward to dd */
|
|
|
|
n->next = target;
|
|
|
|
/* we point back to what dd used to point back to */
|
|
|
|
n->prev = target->prev;
|
|
|
|
/* DDp points forward to us now */
|
|
|
|
if (target->prev)
|
|
|
|
target->prev->next = n;
|
|
|
|
/* DD points back to us now */
|
|
|
|
target->prev = n;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* we go after dd
|
|
|
|
* DDp <-> DD <-> DDn --> DDp <-> DD <-> us <-> DDn
|
|
|
|
*/
|
|
|
|
/* we point forward to what dd used to point forward to */
|
|
|
|
n->next = target->next;
|
|
|
|
/* we point back to dd */
|
|
|
|
n->prev = target;
|
|
|
|
/* DDn points back to us */
|
|
|
|
if (target->next)
|
|
|
|
target->next->prev = n;
|
|
|
|
/* DD points forward to us */
|
|
|
|
target->next = n;
|
|
|
|
}
|
2017-12-05 20:02:29 +08:00
|
|
|
}
|
|
|
|
|
2018-03-19 16:37:37 +08:00
|
|
|
/* situation is:
|
|
|
|
*
|
|
|
|
* HEAD: struct lws_dll * = &entry1
|
|
|
|
*
|
|
|
|
* Entry 1: struct lws_dll .pprev = &HEAD , .next = Entry 2
|
|
|
|
* Entry 2: struct lws_dll .pprev = &entry1 , .next = &entry2
|
|
|
|
* Entry 3: struct lws_dll .pprev = &entry2 , .next = NULL
|
|
|
|
*
|
|
|
|
* Delete Entry1:
|
|
|
|
*
|
|
|
|
* - HEAD = &entry2
|
|
|
|
* - Entry2: .pprev = &HEAD, .next = &entry3
|
|
|
|
* - Entry3: .pprev = &entry2, .next = NULL
|
|
|
|
*
|
|
|
|
* Delete Entry2:
|
|
|
|
*
|
|
|
|
* - HEAD = &entry1
|
|
|
|
* - Entry1: .pprev = &HEAD, .next = &entry3
|
|
|
|
* - Entry3: .pprev = &entry1, .next = NULL
|
|
|
|
*
|
|
|
|
* Delete Entry3:
|
|
|
|
*
|
|
|
|
* - HEAD = &entry1
|
|
|
|
* - Entry1: .pprev = &HEAD, .next = &entry2
|
|
|
|
* - Entry2: .pprev = &entry1, .next = NULL
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2017-02-21 23:38:40 +08:00
|
|
|
void
|
2018-03-19 16:37:37 +08:00
|
|
|
lws_dll_remove(struct lws_dll *d)
|
2016-01-19 04:32:14 +08:00
|
|
|
{
|
2019-03-20 07:39:55 +08:00
|
|
|
if (!d->prev && !d->next)
|
2016-01-19 04:32:14 +08:00
|
|
|
return;
|
|
|
|
|
2018-03-19 16:37:37 +08:00
|
|
|
/*
|
|
|
|
* remove us
|
|
|
|
*
|
|
|
|
* USp <-> us <-> USn --> USp <-> USn
|
|
|
|
*/
|
|
|
|
|
2016-02-25 21:39:01 +08:00
|
|
|
/* if we have a next guy, set his prev to our prev */
|
2018-03-19 16:37:37 +08:00
|
|
|
if (d->next)
|
|
|
|
d->next->prev = d->prev;
|
|
|
|
|
2016-02-25 21:39:01 +08:00
|
|
|
/* set our prev guy to our next guy instead of us */
|
2018-03-19 16:37:37 +08:00
|
|
|
if (d->prev)
|
|
|
|
d->prev->next = d->next;
|
2016-01-19 23:11:39 +08:00
|
|
|
|
2016-02-25 21:39:01 +08:00
|
|
|
/* we're out of the list, we should not point anywhere any more */
|
2018-03-19 16:37:37 +08:00
|
|
|
d->prev = NULL;
|
|
|
|
d->next = NULL;
|
|
|
|
}
|
|
|
|
|
2019-03-19 11:54:27 +08:00
|
|
|
void
|
|
|
|
lws_dll_remove_track_tail(struct lws_dll *d, struct lws_dll *phead)
|
|
|
|
{
|
2019-03-20 07:39:55 +08:00
|
|
|
if (lws_dll_is_detached(d, phead)) {
|
|
|
|
assert(phead->prev != d);
|
|
|
|
assert(phead->next != d);
|
2019-03-19 11:54:27 +08:00
|
|
|
return;
|
2019-03-20 07:39:55 +08:00
|
|
|
}
|
2019-03-19 11:54:27 +08:00
|
|
|
|
2019-03-20 07:39:55 +08:00
|
|
|
/* if we have a next guy, set his prev to our prev */
|
|
|
|
if (d->next)
|
|
|
|
d->next->prev = d->prev;
|
|
|
|
|
|
|
|
/* if we have a previous guy, set his next to our next */
|
|
|
|
if (d->prev)
|
|
|
|
d->prev->next = d->next;
|
2019-03-19 11:54:27 +08:00
|
|
|
|
2019-03-20 07:39:55 +08:00
|
|
|
if (phead->prev == d)
|
2019-03-19 11:54:27 +08:00
|
|
|
phead->prev = d->prev;
|
|
|
|
|
2019-03-20 07:39:55 +08:00
|
|
|
if (phead->next == d)
|
|
|
|
phead->next = d->next;
|
|
|
|
|
|
|
|
/* we're out of the list, we should not point anywhere any more */
|
|
|
|
d->prev = NULL;
|
|
|
|
d->next = NULL;
|
2019-03-19 11:54:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-03-18 16:12:45 +08:00
|
|
|
int
|
2019-03-21 07:04:44 +08:00
|
|
|
lws_dll_foreach_safe(struct lws_dll *phead, void *user,
|
|
|
|
int (*cb)(struct lws_dll *d, void *user))
|
2019-03-18 16:12:45 +08:00
|
|
|
{
|
|
|
|
lws_start_foreach_dll_safe(struct lws_dll *, p, tp, phead->next) {
|
2019-03-21 07:04:44 +08:00
|
|
|
if (cb(p, user))
|
2019-03-18 16:12:45 +08:00
|
|
|
return 1;
|
|
|
|
} lws_end_foreach_dll_safe(p, tp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-20 07:39:55 +08:00
|
|
|
int
|
2019-03-21 07:04:44 +08:00
|
|
|
lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user,
|
|
|
|
int (*cb)(struct lws_dll2 *d, void *user))
|
2019-03-20 07:39:55 +08:00
|
|
|
{
|
|
|
|
lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, owner->head) {
|
2019-03-21 07:04:44 +08:00
|
|
|
if (cb(p, user))
|
2019-03-20 07:39:55 +08:00
|
|
|
return 1;
|
|
|
|
} lws_end_foreach_dll_safe(p, tp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_dll2_add_head(struct lws_dll2 *d, struct lws_dll2_owner *owner)
|
|
|
|
{
|
|
|
|
if (!lws_dll2_is_detached(d)) {
|
|
|
|
assert(0); /* only wholly detached things can be added */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* our next guy is current first guy, if any */
|
|
|
|
if (owner->head != d)
|
|
|
|
d->next = owner->head;
|
|
|
|
|
|
|
|
/* if there is a next guy, set his prev ptr to our next ptr */
|
|
|
|
if (d->next)
|
|
|
|
d->next->prev = d;
|
|
|
|
/* there is nobody previous to us, we are the head */
|
|
|
|
d->prev = NULL;
|
|
|
|
|
|
|
|
/* set the first guy to be us */
|
|
|
|
owner->head = d;
|
|
|
|
|
|
|
|
if (!owner->tail)
|
|
|
|
owner->tail = d;
|
|
|
|
|
|
|
|
d->owner = owner;
|
|
|
|
owner->count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_dll2_add_tail(struct lws_dll2 *d, struct lws_dll2_owner *owner)
|
|
|
|
{
|
|
|
|
if (!lws_dll2_is_detached(d)) {
|
|
|
|
assert(0); /* only wholly detached things can be added */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* our previous guy is current last guy */
|
|
|
|
d->prev = owner->tail;
|
|
|
|
/* if there is a prev guy, set his next ptr to our prev ptr */
|
|
|
|
if (d->prev)
|
|
|
|
d->prev->next = d;
|
|
|
|
/* our next ptr is NULL */
|
|
|
|
d->next = NULL;
|
|
|
|
/* set the last guy to be us */
|
|
|
|
owner->tail = d;
|
|
|
|
|
|
|
|
/* list head is also us if we're the first */
|
|
|
|
if (!owner->head)
|
|
|
|
owner->head = d;
|
|
|
|
|
|
|
|
d->owner = owner;
|
|
|
|
owner->count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_dll2_remove(struct lws_dll2 *d)
|
|
|
|
{
|
|
|
|
if (lws_dll2_is_detached(d))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* if we have a next guy, set his prev to our prev */
|
|
|
|
if (d->next)
|
|
|
|
d->next->prev = d->prev;
|
|
|
|
|
|
|
|
/* if we have a previous guy, set his next to our next */
|
|
|
|
if (d->prev)
|
|
|
|
d->prev->next = d->next;
|
|
|
|
|
|
|
|
/* if we have phead, track the tail and head if it points to us... */
|
|
|
|
|
|
|
|
if (d->owner->tail == d)
|
|
|
|
d->owner->tail = d->prev;
|
|
|
|
|
|
|
|
if (d->owner->head == d)
|
|
|
|
d->owner->head = d->next;
|
|
|
|
|
|
|
|
d->owner->count--;
|
|
|
|
|
|
|
|
/* we're out of the list, we should not point anywhere any more */
|
|
|
|
d->owner = NULL;
|
|
|
|
d->prev = NULL;
|
|
|
|
d->next = NULL;
|
|
|
|
}
|
|
|
|
|
2019-01-15 06:59:48 +08:00
|
|
|
#if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK))
|
|
|
|
|
2018-09-04 08:06:46 +08:00
|
|
|
LWS_VISIBLE lws_usec_t
|
|
|
|
lws_now_usecs(void)
|
|
|
|
{
|
|
|
|
struct timeval now;
|
|
|
|
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
return (now.tv_sec * 1000000ll) + now.tv_usec;
|
|
|
|
}
|
2019-01-15 06:59:48 +08:00
|
|
|
#endif
|
2018-09-04 08:06:46 +08:00
|
|
|
|
2018-11-15 16:33:54 +08:00
|
|
|
int
|
|
|
|
lws_pthread_self_to_tsi(struct lws_context *context)
|
|
|
|
{
|
|
|
|
#if LWS_MAX_SMP > 1
|
|
|
|
pthread_t ps = pthread_self();
|
|
|
|
struct lws_context_per_thread *pt = &context->pt[0];
|
|
|
|
int n;
|
|
|
|
|
|
|
|
for (n = 0; n < context->count_threads; n++) {
|
|
|
|
if (pthread_equal(ps, pt->self))
|
|
|
|
return n;
|
|
|
|
pt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
|
|
|
|
/* lws_buflist */
|
|
|
|
|
|
|
|
int
|
|
|
|
lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf,
|
|
|
|
size_t len)
|
2017-12-07 10:16:17 +08:00
|
|
|
{
|
2019-01-13 06:58:21 +08:00
|
|
|
struct lws_buflist *nbuf;
|
|
|
|
int first = !*head;
|
|
|
|
void *p = *head;
|
|
|
|
int sanity = 1024;
|
2017-12-07 10:16:17 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
assert(buf);
|
|
|
|
assert(len);
|
|
|
|
|
|
|
|
/* append at the tail */
|
|
|
|
while (*head) {
|
|
|
|
if (!--sanity) {
|
|
|
|
lwsl_err("%s: buflist reached sanity limit\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (*head == (*head)->next) {
|
|
|
|
lwsl_err("%s: corrupt list points to self\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
head = &((*head)->next);
|
|
|
|
}
|
2017-12-07 10:16:17 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
lwsl_info("%s: len %u first %d %p\n", __func__, (uint32_t)len, first, p);
|
2018-11-15 16:33:54 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
nbuf = (struct lws_buflist *)lws_malloc(sizeof(**head) + len, __func__);
|
|
|
|
if (!nbuf) {
|
|
|
|
lwsl_err("%s: OOM\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-11-15 16:33:54 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
nbuf->len = len;
|
|
|
|
nbuf->pos = 0;
|
|
|
|
nbuf->next = NULL;
|
2017-12-07 10:16:17 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
p = (void *)nbuf->buf;
|
|
|
|
memcpy(p, buf, len);
|
2018-11-15 16:33:54 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
*head = nbuf;
|
2017-12-07 10:16:17 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
return first; /* returns 1 if first segment just created */
|
2017-12-07 10:16:17 +08:00
|
|
|
}
|
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
static int
|
|
|
|
lws_buflist_destroy_segment(struct lws_buflist **head)
|
2017-03-03 07:36:08 +08:00
|
|
|
{
|
2019-01-13 06:58:21 +08:00
|
|
|
struct lws_buflist *old = *head;
|
2017-03-03 07:36:08 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
assert(*head);
|
|
|
|
*head = old->next;
|
|
|
|
old->next = NULL;
|
|
|
|
lws_free(old);
|
2017-03-30 08:31:36 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
return !*head; /* returns 1 if last segment just destroyed */
|
|
|
|
}
|
2017-07-19 04:39:14 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
void
|
|
|
|
lws_buflist_destroy_all_segments(struct lws_buflist **head)
|
|
|
|
{
|
|
|
|
struct lws_buflist *p = *head, *p1;
|
2017-07-19 04:39:14 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
while (p) {
|
|
|
|
p1 = p->next;
|
|
|
|
p->next = NULL;
|
|
|
|
lws_free(p);
|
|
|
|
p = p1;
|
2017-03-03 07:36:08 +08:00
|
|
|
}
|
2017-03-30 08:31:36 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
*head = NULL;
|
2017-03-03 07:36:08 +08:00
|
|
|
}
|
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
size_t
|
|
|
|
lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf)
|
2017-03-07 16:06:05 +08:00
|
|
|
{
|
2019-01-13 06:58:21 +08:00
|
|
|
if (!*head) {
|
|
|
|
if (buf)
|
|
|
|
*buf = NULL;
|
2017-04-06 13:49:17 +08:00
|
|
|
|
2017-03-07 16:06:05 +08:00
|
|
|
return 0;
|
2019-01-13 06:58:21 +08:00
|
|
|
}
|
2017-03-07 16:06:05 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
if (!(*head)->len && (*head)->next)
|
|
|
|
lws_buflist_destroy_segment(head);
|
2017-04-06 13:49:17 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
if (!*head) {
|
|
|
|
if (buf)
|
|
|
|
*buf = NULL;
|
2017-04-06 13:49:17 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
return 0;
|
2017-04-06 13:49:17 +08:00
|
|
|
}
|
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
assert((*head)->pos < (*head)->len);
|
2017-03-07 16:06:05 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
if (buf)
|
|
|
|
*buf = (*head)->buf + (*head)->pos;
|
2018-04-03 10:37:14 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
return (*head)->len - (*head)->pos;
|
2017-03-07 16:06:05 +08:00
|
|
|
}
|
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
int
|
|
|
|
lws_buflist_use_segment(struct lws_buflist **head, size_t len)
|
2010-11-03 11:13:06 +00:00
|
|
|
{
|
2019-01-13 06:58:21 +08:00
|
|
|
assert(*head);
|
|
|
|
assert(len);
|
|
|
|
assert((*head)->pos + len <= (*head)->len);
|
2017-07-19 04:39:14 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
(*head)->pos += len;
|
|
|
|
if ((*head)->pos == (*head)->len)
|
|
|
|
lws_buflist_destroy_segment(head);
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
if (!*head)
|
|
|
|
return 0;
|
2016-04-15 12:00:23 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
return (int)((*head)->len - (*head)->pos);
|
|
|
|
}
|
2010-11-03 11:13:06 +00:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
void
|
|
|
|
lws_buflist_describe(struct lws_buflist **head, void *id)
|
|
|
|
{
|
|
|
|
struct lws_buflist *old;
|
|
|
|
int n = 0;
|
2018-03-27 09:17:19 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
if (*head == NULL)
|
|
|
|
lwsl_notice("%p: buflist empty\n", id);
|
2018-04-17 15:35:15 +08:00
|
|
|
|
|
|
|
while (*head) {
|
|
|
|
lwsl_notice("%p: %d: %llu / %llu (%llu left)\n", id, n,
|
|
|
|
(unsigned long long)(*head)->pos,
|
|
|
|
(unsigned long long)(*head)->len,
|
|
|
|
(unsigned long long)(*head)->len - (*head)->pos);
|
2018-04-30 19:17:32 +08:00
|
|
|
old = *head;
|
2018-04-17 15:35:15 +08:00
|
|
|
head = &((*head)->next);
|
2018-04-30 19:17:32 +08:00
|
|
|
if (*head == old) {
|
|
|
|
lwsl_err("%s: next points to self\n", __func__);
|
|
|
|
break;
|
|
|
|
}
|
2018-04-17 15:35:15 +08:00
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
LWS_EXTERN void *
|
|
|
|
lws_context_user(struct lws_context *context)
|
2013-01-17 16:50:35 +08:00
|
|
|
{
|
2019-01-13 06:58:21 +08:00
|
|
|
return context->user_space;
|
2013-01-17 16:50:35 +08:00
|
|
|
}
|
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
LWS_VISIBLE void
|
|
|
|
lws_explicit_bzero(void *p, size_t len)
|
2013-10-24 22:12:03 +08:00
|
|
|
{
|
2019-01-13 06:58:21 +08:00
|
|
|
volatile uint8_t *vp = p;
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
while (len--)
|
|
|
|
*vp++ = 0;
|
|
|
|
}
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2019-01-15 06:59:48 +08:00
|
|
|
#if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK))
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
/**
|
|
|
|
* lws_now_secs() - seconds since 1970-1-1
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
LWS_VISIBLE LWS_EXTERN unsigned long
|
|
|
|
lws_now_secs(void)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
gettimeofday(&tv, NULL);
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
return tv.tv_sec;
|
|
|
|
}
|
2013-10-24 22:12:03 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_compare_time_t(struct lws_context *context, time_t t1, time_t t2)
|
|
|
|
{
|
|
|
|
if (t1 < context->time_discontiguity)
|
|
|
|
t1 += context->time_fixup;
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
if (t2 < context->time_discontiguity)
|
|
|
|
t2 += context->time_fixup;
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
return (int)(t1 - t2);
|
|
|
|
}
|
2019-01-15 06:59:48 +08:00
|
|
|
#endif
|
2019-01-13 06:58:21 +08:00
|
|
|
LWS_VISIBLE extern const char *
|
|
|
|
lws_canonical_hostname(struct lws_context *context)
|
|
|
|
{
|
|
|
|
return (const char *)context->canonical_hostname;
|
2013-10-24 22:12:03 +08:00
|
|
|
}
|
|
|
|
|
2017-05-05 11:38:34 -04:00
|
|
|
#if defined(LWS_WITH_SOCKS5)
|
|
|
|
LWS_VISIBLE int
|
|
|
|
lws_set_socks(struct lws_vhost *vhost, const char *socks)
|
|
|
|
{
|
|
|
|
char *p_at, *p_colon;
|
|
|
|
char user[96];
|
|
|
|
char password[96];
|
|
|
|
|
|
|
|
if (!socks)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
vhost->socks_user[0] = '\0';
|
|
|
|
vhost->socks_password[0] = '\0';
|
|
|
|
|
2018-04-20 10:49:29 +08:00
|
|
|
p_at = strrchr(socks, '@');
|
2017-05-05 11:38:34 -04:00
|
|
|
if (p_at) { /* auth is around */
|
|
|
|
if ((unsigned int)(p_at - socks) > (sizeof(user)
|
|
|
|
+ sizeof(password) - 2)) {
|
|
|
|
lwsl_err("Socks auth too long\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
p_colon = strchr(socks, ':');
|
|
|
|
if (p_colon) {
|
|
|
|
if ((unsigned int)(p_colon - socks) > (sizeof(user)
|
|
|
|
- 1) ) {
|
|
|
|
lwsl_err("Socks user too long\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
if ((unsigned int)(p_at - p_colon) > (sizeof(password)
|
|
|
|
- 1) ) {
|
|
|
|
lwsl_err("Socks password too long\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
2017-09-23 13:27:11 +08:00
|
|
|
|
2018-03-12 09:28:26 +08:00
|
|
|
lws_strncpy(vhost->socks_user, socks, p_colon - socks + 1);
|
|
|
|
lws_strncpy(vhost->socks_password, p_colon + 1,
|
|
|
|
p_at - (p_colon + 1) + 1);
|
2017-05-05 11:38:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
lwsl_info(" Socks auth, user: %s, password: %s\n",
|
|
|
|
vhost->socks_user, vhost->socks_password );
|
|
|
|
|
|
|
|
socks = p_at + 1;
|
|
|
|
}
|
|
|
|
|
2018-03-12 09:28:26 +08:00
|
|
|
lws_strncpy(vhost->socks_proxy_address, socks,
|
|
|
|
sizeof(vhost->socks_proxy_address));
|
2017-05-05 11:38:34 -04:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
p_colon = strchr(vhost->socks_proxy_address, ':');
|
|
|
|
if (!p_colon && !vhost->socks_proxy_port) {
|
|
|
|
lwsl_err("socks_proxy needs to be address:port\n");
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
if (p_colon) {
|
|
|
|
*p_colon = '\0';
|
|
|
|
vhost->socks_proxy_port = atoi(p_colon + 1);
|
|
|
|
}
|
2015-12-28 14:24:49 +08:00
|
|
|
}
|
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
lwsl_info(" Socks %s:%u\n", vhost->socks_proxy_address,
|
|
|
|
vhost->socks_proxy_port);
|
2015-12-28 14:24:49 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
return 0;
|
2015-12-28 14:24:49 +08:00
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
bail:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
2015-12-28 14:24:49 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
2019-01-13 06:58:21 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_get_count_threads(struct lws_context *context)
|
|
|
|
{
|
|
|
|
return context->count_threads;
|
2015-12-28 14:24:49 +08:00
|
|
|
}
|
2015-12-30 11:43:36 +08:00
|
|
|
|
2018-10-09 10:29:42 +08:00
|
|
|
static const unsigned char e0f4[] = {
|
|
|
|
0xa0 | ((2 - 1) << 2) | 1, /* e0 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e1 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e2 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e3 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e4 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e5 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e6 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e7 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e8 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e9 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* ea */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* eb */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* ec */
|
|
|
|
0x80 | ((2 - 1) << 2) | 1, /* ed */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* ee */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* ef */
|
|
|
|
0x90 | ((3 - 1) << 2) | 2, /* f0 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 2, /* f1 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 2, /* f2 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 2, /* f3 */
|
|
|
|
0x80 | ((1 - 1) << 2) | 2, /* f4 */
|
|
|
|
|
|
|
|
0, /* s0 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 0, /* s2 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* s3 */
|
|
|
|
};
|
|
|
|
|
|
|
|
LWS_EXTERN int
|
|
|
|
lws_check_byte_utf8(unsigned char state, unsigned char c)
|
|
|
|
{
|
|
|
|
unsigned char s = state;
|
|
|
|
|
|
|
|
if (!s) {
|
|
|
|
if (c >= 0x80) {
|
|
|
|
if (c < 0xc2 || c > 0xf4)
|
|
|
|
return -1;
|
|
|
|
if (c < 0xe0)
|
|
|
|
return 0x80 | ((4 - 1) << 2);
|
|
|
|
else
|
|
|
|
return e0f4[c - 0xe0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
if (c < (s & 0xf0) || c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return e0f4[21 + (s & 3)];
|
|
|
|
}
|
|
|
|
|
2015-12-30 11:43:36 +08:00
|
|
|
LWS_EXTERN int
|
|
|
|
lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len)
|
|
|
|
{
|
|
|
|
unsigned char s = *state;
|
|
|
|
|
|
|
|
while (len--) {
|
|
|
|
unsigned char c = *buf++;
|
|
|
|
|
|
|
|
if (!s) {
|
|
|
|
if (c >= 0x80) {
|
|
|
|
if (c < 0xc2 || c > 0xf4)
|
|
|
|
return 1;
|
|
|
|
if (c < 0xe0)
|
|
|
|
s = 0x80 | ((4 - 1) << 2);
|
|
|
|
else
|
|
|
|
s = e0f4[c - 0xe0];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (c < (s & 0xf0) ||
|
|
|
|
c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30))
|
|
|
|
return 1;
|
|
|
|
s = e0f4[21 + (s & 3)];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*state = s;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-01-11 11:34:01 +08:00
|
|
|
|
2016-01-14 13:39:02 +08:00
|
|
|
|
2018-05-10 16:13:26 +08:00
|
|
|
char *
|
|
|
|
lws_strdup(const char *s)
|
|
|
|
{
|
|
|
|
char *d = lws_malloc(strlen(s) + 1, "strdup");
|
|
|
|
|
|
|
|
if (d)
|
|
|
|
strcpy(d, s);
|
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2016-06-08 10:07:02 +08:00
|
|
|
static const char *hex = "0123456789ABCDEF";
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_sql_purify(char *escaped, const char *string, int len)
|
|
|
|
{
|
|
|
|
const char *p = string;
|
|
|
|
char *q = escaped;
|
|
|
|
|
|
|
|
while (*p && len-- > 2) {
|
|
|
|
if (*p == '\'') {
|
2016-05-19 15:28:31 +08:00
|
|
|
*q++ = '\'';
|
2016-06-08 10:07:02 +08:00
|
|
|
*q++ = '\'';
|
|
|
|
len --;
|
|
|
|
p++;
|
|
|
|
} else
|
|
|
|
*q++ = *p++;
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
|
2016-06-17 10:05:23 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_json_purify(char *escaped, const char *string, int len)
|
|
|
|
{
|
|
|
|
const char *p = string;
|
|
|
|
char *q = escaped;
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (!p) {
|
|
|
|
escaped[0] = '\0';
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
|
2016-06-17 10:05:23 +08:00
|
|
|
while (*p && len-- > 6) {
|
2018-08-14 08:00:25 +08:00
|
|
|
if (*p == '\t') {
|
|
|
|
p++;
|
|
|
|
*q++ = '\\';
|
|
|
|
*q++ = 't';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*p == '\n') {
|
|
|
|
p++;
|
|
|
|
*q++ = '\\';
|
|
|
|
*q++ = 'n';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*p == '\r') {
|
|
|
|
p++;
|
|
|
|
*q++ = '\\';
|
|
|
|
*q++ = 'r';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-06-17 10:05:23 +08:00
|
|
|
if (*p == '\"' || *p == '\\' || *p < 0x20) {
|
|
|
|
*q++ = '\\';
|
|
|
|
*q++ = 'u';
|
|
|
|
*q++ = '0';
|
|
|
|
*q++ = '0';
|
|
|
|
*q++ = hex[((*p) >> 4) & 15];
|
|
|
|
*q++ = hex[(*p) & 15];
|
|
|
|
len -= 5;
|
|
|
|
p++;
|
|
|
|
} else
|
|
|
|
*q++ = *p++;
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
|
2018-03-29 09:28:41 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
|
|
lws_filename_purify_inplace(char *filename)
|
|
|
|
{
|
|
|
|
while (*filename) {
|
|
|
|
|
|
|
|
if (*filename == '.' && filename[1] == '.') {
|
|
|
|
*filename = '_';
|
|
|
|
filename[1] = '_';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*filename == ':' ||
|
|
|
|
*filename == '\\' ||
|
|
|
|
*filename == '$' ||
|
|
|
|
*filename == '%')
|
|
|
|
*filename = '_';
|
|
|
|
|
|
|
|
filename++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-08 10:07:02 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_urlencode(char *escaped, const char *string, int len)
|
|
|
|
{
|
|
|
|
const char *p = string;
|
|
|
|
char *q = escaped;
|
|
|
|
|
|
|
|
while (*p && len-- > 3) {
|
|
|
|
if (*p == ' ') {
|
|
|
|
*q++ = '+';
|
|
|
|
p++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((*p >= '0' && *p <= '9') ||
|
|
|
|
(*p >= 'A' && *p <= 'Z') ||
|
|
|
|
(*p >= 'a' && *p <= 'z')) {
|
|
|
|
*q++ = *p++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*q++ = '%';
|
|
|
|
*q++ = hex[(*p >> 4) & 0xf];
|
|
|
|
*q++ = hex[*p & 0xf];
|
|
|
|
|
|
|
|
len -= 2;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_urldecode(char *string, const char *escaped, int len)
|
|
|
|
{
|
|
|
|
int state = 0, n;
|
|
|
|
char sum = 0;
|
|
|
|
|
|
|
|
while (*escaped && len) {
|
|
|
|
switch (state) {
|
|
|
|
case 0:
|
|
|
|
if (*escaped == '%') {
|
|
|
|
state++;
|
|
|
|
escaped++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*escaped == '+') {
|
|
|
|
escaped++;
|
|
|
|
*string++ = ' ';
|
|
|
|
len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*string++ = *escaped++;
|
|
|
|
len--;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
n = char_to_hex(*escaped);
|
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
escaped++;
|
|
|
|
sum = n << 4;
|
|
|
|
state++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
n = char_to_hex(*escaped);
|
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
escaped++;
|
|
|
|
*string++ = sum | n;
|
|
|
|
len--;
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
*string = '\0';
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_finalize_startup(struct lws_context *context)
|
|
|
|
{
|
|
|
|
struct lws_context_creation_info info;
|
|
|
|
|
|
|
|
info.uid = context->uid;
|
|
|
|
info.gid = context->gid;
|
|
|
|
|
2017-06-07 06:10:02 +08:00
|
|
|
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
|
|
|
|
memcpy(info.caps, context->caps, sizeof(info.caps));
|
|
|
|
info.count_caps = context->count_caps;
|
|
|
|
#endif
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
|
|
|
|
lws_plat_drop_app_privileges(&info);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-02 19:15:19 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
|
|
lws_get_effective_uid_gid(struct lws_context *context, int *uid, int *gid)
|
|
|
|
{
|
|
|
|
*uid = context->uid;
|
|
|
|
*gid = context->gid;
|
|
|
|
}
|
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
int
|
|
|
|
lws_snprintf(char *str, size_t size, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if (!size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
va_start(ap, format);
|
|
|
|
n = vsnprintf(str, size, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
2017-06-09 20:20:42 +08:00
|
|
|
if (n >= (int)size)
|
2017-10-25 08:00:23 +08:00
|
|
|
return (int)size;
|
2016-09-15 02:22:57 +08:00
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2018-03-12 09:28:26 +08:00
|
|
|
char *
|
|
|
|
lws_strncpy(char *dest, const char *src, size_t size)
|
|
|
|
{
|
|
|
|
strncpy(dest, src, size - 1);
|
|
|
|
dest[size - 1] = '\0';
|
|
|
|
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
2018-12-13 20:05:12 +08:00
|
|
|
int
|
|
|
|
lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len)
|
|
|
|
{
|
|
|
|
const uint8_t *pa = a, *pb = b;
|
|
|
|
uint8_t sum = 0;
|
|
|
|
|
|
|
|
while (len--)
|
|
|
|
sum |= (*pa++ ^ *pb++);
|
|
|
|
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
2018-10-09 10:29:42 +08:00
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
LWS_TOKZS_LEADING_WHITESPACE,
|
|
|
|
LWS_TOKZS_QUOTED_STRING,
|
|
|
|
LWS_TOKZS_TOKEN,
|
|
|
|
LWS_TOKZS_TOKEN_POST_TERMINAL
|
|
|
|
} lws_tokenize_state;
|
|
|
|
|
|
|
|
int
|
|
|
|
lws_tokenize(struct lws_tokenize *ts)
|
|
|
|
{
|
|
|
|
const char *rfc7230_delims = "(),/:;<=>?@[\\]{}";
|
|
|
|
lws_tokenize_state state = LWS_TOKZS_LEADING_WHITESPACE;
|
2018-11-13 09:34:10 +08:00
|
|
|
char c, flo = 0, d_minus = '-', d_dot = '.', s_minus = '\0',
|
|
|
|
s_dot = '\0';
|
2018-10-26 13:48:31 +08:00
|
|
|
signed char num = -1;
|
2018-10-09 10:29:42 +08:00
|
|
|
int utf8 = 0;
|
|
|
|
|
2018-11-13 09:34:10 +08:00
|
|
|
/* for speed, compute the effect of the flags outside the loop */
|
|
|
|
|
|
|
|
if (ts->flags & LWS_TOKENIZE_F_MINUS_NONTERM) {
|
|
|
|
d_minus = '\0';
|
|
|
|
s_minus = '-';
|
|
|
|
}
|
|
|
|
if (ts->flags & LWS_TOKENIZE_F_DOT_NONTERM) {
|
|
|
|
d_dot = '\0';
|
|
|
|
s_dot = '.';
|
|
|
|
}
|
|
|
|
|
2018-10-09 10:29:42 +08:00
|
|
|
ts->token = NULL;
|
|
|
|
ts->token_len = 0;
|
|
|
|
|
|
|
|
while (ts->len) {
|
|
|
|
c = *ts->start++;
|
|
|
|
ts->len--;
|
|
|
|
|
|
|
|
utf8 = lws_check_byte_utf8((unsigned char)utf8, c);
|
|
|
|
if (utf8 < 0)
|
|
|
|
return LWS_TOKZE_ERR_BROKEN_UTF8;
|
|
|
|
|
|
|
|
if (!c)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* whitespace */
|
|
|
|
|
|
|
|
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' ||
|
|
|
|
c == '\f') {
|
|
|
|
switch (state) {
|
|
|
|
case LWS_TOKZS_LEADING_WHITESPACE:
|
|
|
|
case LWS_TOKZS_TOKEN_POST_TERMINAL:
|
|
|
|
continue;
|
|
|
|
case LWS_TOKZS_QUOTED_STRING:
|
|
|
|
ts->token_len++;
|
|
|
|
continue;
|
|
|
|
case LWS_TOKZS_TOKEN:
|
|
|
|
/* we want to scan forward to look for = */
|
|
|
|
|
|
|
|
state = LWS_TOKZS_TOKEN_POST_TERMINAL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* quoted string */
|
|
|
|
|
|
|
|
if (c == '\"') {
|
|
|
|
if (state == LWS_TOKZS_QUOTED_STRING)
|
|
|
|
return LWS_TOKZE_QUOTED_STRING;
|
|
|
|
|
|
|
|
/* starting a quoted string */
|
|
|
|
|
|
|
|
if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
|
|
|
|
if (ts->delim == LWSTZ_DT_NEED_DELIM)
|
|
|
|
return LWS_TOKZE_ERR_COMMA_LIST;
|
|
|
|
ts->delim = LWSTZ_DT_NEED_DELIM;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = LWS_TOKZS_QUOTED_STRING;
|
|
|
|
ts->token = ts->start;
|
|
|
|
ts->token_len = 0;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* token= aggregation */
|
|
|
|
|
|
|
|
if (c == '=' && (state == LWS_TOKZS_TOKEN_POST_TERMINAL ||
|
|
|
|
state == LWS_TOKZS_TOKEN)) {
|
|
|
|
if (num == 1)
|
|
|
|
return LWS_TOKZE_ERR_NUM_ON_LHS;
|
|
|
|
/* swallow the = */
|
|
|
|
return LWS_TOKZE_TOKEN_NAME_EQUALS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* optional token: aggregation */
|
|
|
|
|
|
|
|
if ((ts->flags & LWS_TOKENIZE_F_AGG_COLON) && c == ':' &&
|
|
|
|
(state == LWS_TOKZS_TOKEN_POST_TERMINAL ||
|
|
|
|
state == LWS_TOKZS_TOKEN))
|
|
|
|
/* swallow the : */
|
|
|
|
return LWS_TOKZE_TOKEN_NAME_COLON;
|
|
|
|
|
|
|
|
/* aggregate . in a number as a float */
|
|
|
|
|
2018-11-13 09:34:10 +08:00
|
|
|
if (c == '.' && !(ts->flags & LWS_TOKENIZE_F_NO_FLOATS) &&
|
|
|
|
state == LWS_TOKZS_TOKEN && num == 1) {
|
2018-10-09 10:29:42 +08:00
|
|
|
if (flo)
|
|
|
|
return LWS_TOKZE_ERR_MALFORMED_FLOAT;
|
|
|
|
flo = 1;
|
|
|
|
ts->token_len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delimiter... by default anything that:
|
|
|
|
*
|
|
|
|
* - isn't matched earlier, or
|
|
|
|
* - is [A-Z, a-z, 0-9, _], and
|
|
|
|
* - is not a partial utf8 char
|
|
|
|
*
|
|
|
|
* is a "delimiter", it marks the end of a token and is itself
|
|
|
|
* reported as a single LWS_TOKZE_DELIMITER each time.
|
|
|
|
*
|
|
|
|
* However with LWS_TOKENIZE_F_RFC7230_DELIMS flag, tokens may
|
|
|
|
* contain any noncontrol character that isn't defined in
|
|
|
|
* rfc7230_delims, and only characters listed there are treated
|
|
|
|
* as delimiters.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!utf8 &&
|
|
|
|
((ts->flags & LWS_TOKENIZE_F_RFC7230_DELIMS &&
|
|
|
|
strchr(rfc7230_delims, c) && c > 32) ||
|
|
|
|
((!(ts->flags & LWS_TOKENIZE_F_RFC7230_DELIMS) &&
|
|
|
|
(c < '0' || c > '9') && (c < 'A' || c > 'Z') &&
|
2018-11-13 09:34:10 +08:00
|
|
|
(c < 'a' || c > 'z') && c != '_') &&
|
|
|
|
c != s_minus && c != s_dot) ||
|
|
|
|
c == d_minus || c == d_dot
|
2018-10-09 10:29:42 +08:00
|
|
|
)) {
|
|
|
|
switch (state) {
|
|
|
|
case LWS_TOKZS_LEADING_WHITESPACE:
|
|
|
|
if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
|
|
|
|
if (c != ',' ||
|
|
|
|
ts->delim != LWSTZ_DT_NEED_DELIM)
|
|
|
|
return LWS_TOKZE_ERR_COMMA_LIST;
|
|
|
|
ts->delim = LWSTZ_DT_NEED_NEXT_CONTENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
ts->token = ts->start - 1;
|
|
|
|
ts->token_len = 1;
|
|
|
|
return LWS_TOKZE_DELIMITER;
|
|
|
|
|
|
|
|
case LWS_TOKZS_QUOTED_STRING:
|
|
|
|
ts->token_len++;
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case LWS_TOKZS_TOKEN_POST_TERMINAL:
|
|
|
|
case LWS_TOKZS_TOKEN:
|
|
|
|
/* report the delimiter next time */
|
|
|
|
ts->start--;
|
|
|
|
ts->len++;
|
|
|
|
goto token_or_numeric;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* anything that's not whitespace or delimiter is payload */
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case LWS_TOKZS_LEADING_WHITESPACE:
|
|
|
|
|
|
|
|
if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
|
|
|
|
if (ts->delim == LWSTZ_DT_NEED_DELIM)
|
|
|
|
return LWS_TOKZE_ERR_COMMA_LIST;
|
|
|
|
ts->delim = LWSTZ_DT_NEED_DELIM;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = LWS_TOKZS_TOKEN;
|
|
|
|
ts->token = ts->start - 1;
|
|
|
|
ts->token_len = 1;
|
|
|
|
if (c < '0' || c > '9')
|
|
|
|
num = 0;
|
|
|
|
else
|
|
|
|
if (num < 0)
|
|
|
|
num = 1;
|
|
|
|
continue;
|
|
|
|
case LWS_TOKZS_QUOTED_STRING:
|
|
|
|
case LWS_TOKZS_TOKEN:
|
|
|
|
if (c < '0' || c > '9')
|
|
|
|
num = 0;
|
|
|
|
else
|
|
|
|
if (num < 0)
|
|
|
|
num = 1;
|
|
|
|
ts->token_len++;
|
|
|
|
continue;
|
|
|
|
case LWS_TOKZS_TOKEN_POST_TERMINAL:
|
|
|
|
/* report the new token next time */
|
|
|
|
ts->start--;
|
|
|
|
ts->len++;
|
|
|
|
goto token_or_numeric;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we ran out of content */
|
|
|
|
|
|
|
|
if (utf8) /* ended partway through a multibyte char */
|
|
|
|
return LWS_TOKZE_ERR_BROKEN_UTF8;
|
|
|
|
|
|
|
|
if (state == LWS_TOKZS_QUOTED_STRING)
|
|
|
|
return LWS_TOKZE_ERR_UNTERM_STRING;
|
|
|
|
|
|
|
|
if (state != LWS_TOKZS_TOKEN_POST_TERMINAL &&
|
|
|
|
state != LWS_TOKZS_TOKEN) {
|
|
|
|
if ((ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) &&
|
|
|
|
ts->delim == LWSTZ_DT_NEED_NEXT_CONTENT)
|
|
|
|
return LWS_TOKZE_ERR_COMMA_LIST;
|
|
|
|
|
|
|
|
return LWS_TOKZE_ENDED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* report the pending token */
|
|
|
|
|
|
|
|
token_or_numeric:
|
|
|
|
|
|
|
|
if (num != 1)
|
|
|
|
return LWS_TOKZE_TOKEN;
|
|
|
|
if (flo)
|
|
|
|
return LWS_TOKZE_FLOAT;
|
|
|
|
|
|
|
|
return LWS_TOKZE_INTEGER;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_tokenize_cstr(struct lws_tokenize *ts, char *str, int max)
|
|
|
|
{
|
|
|
|
if (ts->token_len + 1 >= max)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
memcpy(str, ts->token, ts->token_len);
|
|
|
|
str[ts->token_len] = '\0';
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
|
|
lws_tokenize_init(struct lws_tokenize *ts, const char *start, int flags)
|
|
|
|
{
|
|
|
|
ts->start = start;
|
|
|
|
ts->len = 0x7fffffff;
|
|
|
|
ts->flags = flags;
|
|
|
|
ts->delim = LWSTZ_DT_NEED_FIRST_CONTENT;
|
|
|
|
}
|
|
|
|
|
2018-06-27 07:15:39 +08:00
|
|
|
#if LWS_MAX_SMP > 1
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_mutex_refcount_init(struct lws_mutex_refcount *mr)
|
|
|
|
{
|
|
|
|
pthread_mutex_init(&mr->lock, NULL);
|
|
|
|
mr->last_lock_reason = NULL;
|
|
|
|
mr->lock_depth = 0;
|
|
|
|
mr->metadata = 0;
|
|
|
|
mr->lock_owner = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_mutex_refcount_destroy(struct lws_mutex_refcount *mr)
|
|
|
|
{
|
|
|
|
pthread_mutex_destroy(&mr->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_mutex_refcount_lock(struct lws_mutex_refcount *mr, const char *reason)
|
|
|
|
{
|
|
|
|
/* if true, this sequence is atomic because our thread has the lock
|
|
|
|
*
|
|
|
|
* - if true, only guy who can race to make it untrue is our thread,
|
|
|
|
* and we are here.
|
|
|
|
*
|
|
|
|
* - if false, only guy who could race to make it true is our thread,
|
|
|
|
* and we are here
|
|
|
|
*
|
|
|
|
* - it can be false and change to a different tid that is also false
|
|
|
|
*/
|
|
|
|
if (mr->lock_owner == pthread_self()) {
|
|
|
|
/* atomic because we only change it if we own the lock */
|
|
|
|
mr->lock_depth++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_lock(&mr->lock);
|
|
|
|
/* atomic because only we can have the lock */
|
|
|
|
mr->last_lock_reason = reason;
|
|
|
|
mr->lock_owner = pthread_self();
|
|
|
|
mr->lock_depth = 1;
|
|
|
|
//lwsl_notice("tid %d: lock %s\n", mr->tid, reason);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr)
|
|
|
|
{
|
|
|
|
if (--mr->lock_depth)
|
|
|
|
/* atomic because only thread that has the lock can unlock */
|
|
|
|
return;
|
|
|
|
|
|
|
|
mr->last_lock_reason = "free";
|
|
|
|
mr->lock_owner = 0;
|
|
|
|
//lwsl_notice("tid %d: unlock %s\n", mr->tid, mr->last_lock_reason);
|
|
|
|
pthread_mutex_unlock(&mr->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* SMP */
|
2016-09-15 02:22:57 +08:00
|
|
|
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
|
2018-04-12 15:56:38 +08:00
|
|
|
const char *
|
|
|
|
lws_cmdline_option(int argc, const char **argv, const char *val)
|
|
|
|
{
|
2018-04-16 19:52:28 +08:00
|
|
|
int n = (int)strlen(val), c = argc;
|
2018-04-12 15:56:38 +08:00
|
|
|
|
2018-05-05 06:58:52 +08:00
|
|
|
while (--c > 0) {
|
2018-05-05 10:16:54 +08:00
|
|
|
|
2018-04-16 07:32:02 +08:00
|
|
|
if (!strncmp(argv[c], val, n)) {
|
2018-05-05 10:16:54 +08:00
|
|
|
if (!*(argv[c] + n) && c < argc - 1) {
|
|
|
|
/* coverity treats unchecked argv as "tainted" */
|
|
|
|
if (!argv[c + 1] || strlen(argv[c + 1]) > 1024)
|
|
|
|
return NULL;
|
2018-04-12 15:56:38 +08:00
|
|
|
return argv[c + 1];
|
2018-05-05 10:16:54 +08:00
|
|
|
}
|
2018-04-12 15:56:38 +08:00
|
|
|
|
|
|
|
return argv[c] + n;
|
|
|
|
}
|
2018-05-05 06:58:52 +08:00
|
|
|
}
|
2018-04-12 15:56:38 +08:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|