Add libwebsockets as thirdparty library.

License is LGPLv2.1 + static linking exception, version is 2.4.1
This commit is contained in:
Fabio Alessandrelli 2017-12-21 03:09:18 +01:00
parent e7cf2b2e77
commit fa33e0f62d
45 changed files with 35743 additions and 0 deletions

17
thirdparty/README.md vendored
View File

@ -217,6 +217,23 @@ changes to ensure they build for Javascript/HTML5. Those
changes are marked with `// -- GODOT --` comments.
## libwebsockets
- Upstream: https://github.com/warmcat/libwebsockets
- Version: 2.4.1
- License: LGPLv2.1 + static linking exception
File extracted from upstream source:
- Everything in `lib/` except `mbedtls_wrapper/`, `http2/`, `event-libs/`.
- From `misc/` exclude `lws-genhash.c`, `lws-ring.c`, `romfs.{c,h}`, `smtp.c`.
- From `plat/` exclude `lws-plat-{esp*,optee}.c`.
- From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`,
`peer-limits.c`, `rewrite.c`
- Also copy `win32helpers/` from `win32port/`
Important: `lws_config.h` and `lws_config_private.h` contains custom
Godot build configurations, check them out when updating.
## minizip
- Upstream: http://www.zlib.net

555
thirdparty/lws/LICENSE.txt vendored Normal file
View File

@ -0,0 +1,555 @@
Libwebsockets and included programs are provided under the terms of the GNU
Library General Public License (LGPL) 2.1, with the following exceptions:
1) Any reference, whether in these modifications or in the GNU
Library General Public License 2.1, to this License, these terms, the
GNU Lesser Public License, GNU Library General Public License, LGPL, or
any similar reference shall refer to the GNU Library General Public
License 2.1 as modified by these paragraphs 1) through 4).
2) Static linking of programs with the libwebsockets library does not
constitute a derivative work and does not require the author to provide
source code for the program, use the shared libwebsockets libraries, or
link their program against a user-supplied version of libwebsockets.
If you link the program to a modified version of libwebsockets, then the
changes to libwebsockets must be provided under the terms of the LGPL in
sections 1, 2, and 4.
3) You do not have to provide a copy of the libwebsockets license with
programs that are linked to the libwebsockets library, nor do you have to
identify the libwebsockets license in your program or documentation as
required by section 6 of the LGPL.
However, programs must still identify their use of libwebsockets. The
following example statement can be included in user documentation to
satisfy this requirement:
"[program] is based in part on the work of the libwebsockets project
(https://libwebsockets.org)"
4) Some sources included have their own, more liberal licenses, or options
to get original sources with the liberal terms.
Original liberal license retained
- lib/sha-1.c - 3-clause BSD license retained, link to original
- win32port/zlib - ZLIB license (see zlib.h)
Relicensed to libwebsocket license
- lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
- lib/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
link to original Public Domain version
Public Domain (CC-zero) to simplify reuse
- test-server/*.c
- test-server/*.h
- lwsws/*
------ end of exceptions
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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; either
version 2.1 of the License, or (at your option) any later version.
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
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

90
thirdparty/lws/alloc.c vendored Normal file
View File

@ -0,0 +1,90 @@
#include "private-libwebsockets.h"
#if defined(LWS_PLAT_OPTEE)
#define TEE_USER_MEM_HINT_NO_FILL_ZERO 0x80000000
void *__attribute__((weak))
TEE_Malloc(uint32_t size, uint32_t hint)
{
return NULL;
}
void *__attribute__((weak))
TEE_Realloc(void *buffer, uint32_t newSize)
{
return NULL;
}
void __attribute__((weak))
TEE_Free(void *buffer)
{
}
void *lws_realloc(void *ptr, size_t size, const char *reason)
{
return TEE_Realloc(ptr, size);
}
void *lws_malloc(size_t size, const char *reason)
{
return TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO);
}
void lws_free(void *p)
{
TEE_Free(p);
}
void *lws_zalloc(size_t size, const char *reason)
{
void *ptr = TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason))
{
(void)cb;
}
#else
static void *_realloc(void *ptr, size_t size, const char *reason)
{
if (size) {
#if defined(LWS_PLAT_ESP32)
lwsl_notice("%s: size %lu: %s\n", __func__, (unsigned long)size, reason);
#else
lwsl_debug("%s: size %lu: %s\n", __func__, (unsigned long)size, reason);
#endif
#if defined(LWS_PLAT_OPTEE)
return (void *)TEE_Realloc(ptr, size);
#else
return (void *)realloc(ptr, size);
#endif
}
if (ptr)
free(ptr);
return NULL;
}
void *(*_lws_realloc)(void *ptr, size_t size, const char *reason) = _realloc;
void *lws_realloc(void *ptr, size_t size, const char *reason)
{
return _lws_realloc(ptr, size, reason);
}
void *lws_zalloc(size_t size, const char *reason)
{
void *ptr = _lws_realloc(NULL, size, reason);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason))
{
_lws_realloc = cb;
}
#endif

1051
thirdparty/lws/client/client-handshake.c vendored Normal file

File diff suppressed because it is too large Load Diff

598
thirdparty/lws/client/client-parser.c vendored Normal file
View File

@ -0,0 +1,598 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
/*
* parsers.c: lws_rx_sm() needs to be roughly kept in
* sync with changes here, esp related to ext draining
*/
int lws_client_rx_sm(struct lws *wsi, unsigned char c)
{
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
int handled, n, m, rx_draining_ext = 0;
unsigned short close_code;
struct lws_tokens eff_buf;
unsigned char *pp;
if (wsi->u.ws.rx_draining_ext) {
assert(!c);
eff_buf.token = NULL;
eff_buf.token_len = 0;
lws_remove_wsi_from_draining_ext_list(wsi);
rx_draining_ext = 1;
lwsl_debug("%s: doing draining flow\n", __func__);
goto drain_extension;
}
if (wsi->socket_is_permanently_unusable)
return -1;
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
/* control frames (PING) may interrupt checkable sequences */
wsi->u.ws.defeat_check_utf8 = 0;
switch (wsi->ietf_spec_revision) {
case 13:
wsi->u.ws.opcode = c & 0xf;
/* revisit if an extension wants them... */
switch (wsi->u.ws.opcode) {
case LWSWSOPC_TEXT_FRAME:
wsi->u.ws.rsv_first_msg = (c & 0x70);
wsi->u.ws.continuation_possible = 1;
wsi->u.ws.check_utf8 = lws_check_opt(
wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8);
wsi->u.ws.utf8 = 0;
break;
case LWSWSOPC_BINARY_FRAME:
wsi->u.ws.rsv_first_msg = (c & 0x70);
wsi->u.ws.check_utf8 = 0;
wsi->u.ws.continuation_possible = 1;
break;
case LWSWSOPC_CONTINUATION:
if (!wsi->u.ws.continuation_possible) {
lwsl_info("disordered continuation\n");
return -1;
}
break;
case LWSWSOPC_CLOSE:
wsi->u.ws.check_utf8 = 0;
wsi->u.ws.utf8 = 0;
break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 0xb:
case 0xc:
case 0xd:
case 0xe:
case 0xf:
lwsl_info("illegal opcode\n");
return -1;
default:
wsi->u.ws.defeat_check_utf8 = 1;
break;
}
wsi->u.ws.rsv = (c & 0x70);
/* revisit if an extension wants them... */
if (
#ifndef LWS_NO_EXTENSIONS
!wsi->count_act_ext &&
#endif
wsi->u.ws.rsv) {
lwsl_info("illegal rsv bits set\n");
return -1;
}
wsi->u.ws.final = !!((c >> 7) & 1);
lwsl_ext("%s: This RX frame Final %d\n", __func__,
wsi->u.ws.final);
if (wsi->u.ws.owed_a_fin &&
(wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME ||
wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME)) {
lwsl_info("hey you owed us a FIN\n");
return -1;
}
if ((!(wsi->u.ws.opcode & 8)) && wsi->u.ws.final) {
wsi->u.ws.continuation_possible = 0;
wsi->u.ws.owed_a_fin = 0;
}
if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) {
lwsl_info("control msg can't be fragmented\n");
return -1;
}
if (!wsi->u.ws.final)
wsi->u.ws.owed_a_fin = 1;
switch (wsi->u.ws.opcode) {
case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME:
wsi->u.ws.frame_is_binary = wsi->u.ws.opcode ==
LWSWSOPC_BINARY_FRAME;
break;
}
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
break;
default:
lwsl_err("unknown spec version %02d\n",
wsi->ietf_spec_revision);
break;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN:
wsi->u.ws.this_frame_masked = !!(c & 0x80);
switch (c & 0x7f) {
case 126:
/* control frames are not allowed to have big lengths */
if (wsi->u.ws.opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
break;
case 127:
/* control frames are not allowed to have big lengths */
if (wsi->u.ws.opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
break;
default:
wsi->u.ws.rx_packet_length = c;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (c)
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
}
break;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_2:
wsi->u.ws.rx_packet_length = c << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
wsi->u.ws.rx_packet_length |= c;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_8:
if (c & 0x80) {
lwsl_warn("b63 of length must be zero\n");
/* kill the connection */
return -1;
}
#if defined __LP64__
wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
#else
wsi->u.ws.rx_packet_length = 0;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_7:
#if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_6:
#if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_5:
#if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_4:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_3:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_2:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_1:
wsi->u.ws.rx_packet_length |= (size_t)c;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
}
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
wsi->u.ws.mask[0] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
wsi->u.ws.mask[1] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
wsi->u.ws.mask[2] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
wsi->u.ws.mask[3] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
break;
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
assert(wsi->u.ws.rx_ubuf);
if (wsi->u.ws.rx_draining_ext)
goto drain_extension;
if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce)
c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3];
wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c;
if (--wsi->u.ws.rx_packet_length == 0) {
/* spill because we have the whole frame */
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
/*
* if there's no protocol max frame size given, we are
* supposed to default to context->pt_serv_buf_size
*/
if (!wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size)
break;
if (wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size)
break;
/* spill because we filled our rx buffer */
spill:
handled = 0;
/*
* is this frame a control packet we should take care of at this
* layer? If so service it and hide it from the user callback
*/
switch (wsi->u.ws.opcode) {
case LWSWSOPC_CLOSE:
pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE];
if (lws_check_opt(wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8) &&
wsi->u.ws.rx_ubuf_head > 2 &&
lws_check_utf8(&wsi->u.ws.utf8, pp + 2,
wsi->u.ws.rx_ubuf_head - 2))
goto utf8_fail;
/* is this an acknowledgement of our close? */
if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
/*
* fine he has told us he is closing too, let's
* finish our close
*/
lwsl_parser("seen server's close ack\n");
return -1;
}
lwsl_parser("client sees server close len = %d\n",
wsi->u.ws.rx_ubuf_head);
if (wsi->u.ws.rx_ubuf_head >= 2) {
close_code = (pp[0] << 8) | pp[1];
if (close_code < 1000 ||
close_code == 1004 ||
close_code == 1005 ||
close_code == 1006 ||
close_code == 1012 ||
close_code == 1013 ||
close_code == 1014 ||
close_code == 1015 ||
(close_code >= 1016 && close_code < 3000)
) {
pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff;
pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff;
}
}
if (user_callback_handle_rxflow(
wsi->protocol->callback, wsi,
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
wsi->user_space, pp,
wsi->u.ws.rx_ubuf_head))
return -1;
if (lws_partial_buffered(wsi))
/*
* if we're in the middle of something,
* we can't do a normal close response and
* have to just close our end.
*/
wsi->socket_is_permanently_unusable = 1;
else
/*
* parrot the close packet payload back
* we do not care about how it went, we are closing
* immediately afterwards
*/
lws_write(wsi, (unsigned char *)
&wsi->u.ws.rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head,
LWS_WRITE_CLOSE);
wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
/* close the connection */
return -1;
case LWSWSOPC_PING:
lwsl_info("received %d byte ping, sending pong\n",
wsi->u.ws.rx_ubuf_head);
/* he set a close reason on this guy, ignore PING */
if (wsi->u.ws.close_in_ping_buffer_len)
goto ping_drop;
if (wsi->u.ws.ping_pending_flag) {
/*
* there is already a pending ping payload
* we should just log and drop
*/
lwsl_parser("DROP PING since one pending\n");
goto ping_drop;
}
/* control packets can only be < 128 bytes long */
if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
lwsl_parser("DROP PING payload too large\n");
goto ping_drop;
}
/* stash the pong payload */
memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE,
&wsi->u.ws.rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head);
wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head;
wsi->u.ws.ping_pending_flag = 1;
/* get it sent as soon as possible */
lws_callback_on_writable(wsi);
ping_drop:
wsi->u.ws.rx_ubuf_head = 0;
handled = 1;
break;
case LWSWSOPC_PONG:
lwsl_info("client receied pong\n");
lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head);
if (wsi->pending_timeout ==
PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
lwsl_info("%p: received expected PONG\n", wsi);
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
}
/* issue it */
callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
break;
case LWSWSOPC_CONTINUATION:
case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME:
break;
default:
lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode);
/*
* It's something special we can't understand here.
* Pass the payload up to the extension's parsing
* state machine.
*/
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
if (lws_ext_cb_active(wsi,
LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
&eff_buf, 0) <= 0) {
/* not handled or failed */
lwsl_ext("Unhandled ext opc 0x%x\n",
wsi->u.ws.opcode);
wsi->u.ws.rx_ubuf_head = 0;
return 0;
}
handled = 1;
break;
}
/*
* No it's real payload, pass it up to the user callback.
* It's nicely buffered with the pre-padding taken care of
* so it can be sent straight out again using lws_write
*/
if (handled)
goto already_done;
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len)
goto already_done;
drain_extension:
lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
lwsl_ext("Ext RX returned %d\n", n);
if (n < 0) {
wsi->socket_is_permanently_unusable = 1;
return -1;
}
lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len);
if (rx_draining_ext && !eff_buf.token_len) {
lwsl_debug(" --- ending drain on 0 read result\n");
goto already_done;
}
if (wsi->u.ws.check_utf8 && !wsi->u.ws.defeat_check_utf8) {
if (lws_check_utf8(&wsi->u.ws.utf8,
(unsigned char *)eff_buf.token,
eff_buf.token_len))
goto utf8_fail;
/* we are ending partway through utf-8 character? */
if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final &&
wsi->u.ws.utf8 && !n) {
lwsl_info("FINAL utf8 error\n");
utf8_fail:
lwsl_info("utf8 error\n");
return -1;
}
}
if (eff_buf.token_len < 0 &&
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
goto already_done;
if (!eff_buf.token)
goto already_done;
eff_buf.token[eff_buf.token_len] = '\0';
if (!wsi->protocol->callback)
goto already_done;
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
lwsl_info("Client doing pong callback\n");
if (n && eff_buf.token_len)
/* extension had more... main loop will come back
* we want callback to be done with this set, if so,
* because lws_is_final() hides it was final until the
* last chunk
*/
lws_add_wsi_to_draining_ext_list(wsi);
else
lws_remove_wsi_from_draining_ext_list(wsi);
if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
wsi->state == LWSS_AWAITING_CLOSE_ACK)
goto already_done;
m = wsi->protocol->callback(wsi,
(enum lws_callback_reasons)callback_action,
wsi->user_space, eff_buf.token, eff_buf.token_len);
/* if user code wants to close, let caller know */
if (m)
return 1;
already_done:
wsi->u.ws.rx_ubuf_head = 0;
break;
default:
lwsl_err("client rx illegal state\n");
return 1;
}
return 0;
illegal_ctl_length:
lwsl_warn("Control frame asking for extended length is illegal\n");
/* kill the connection */
return -1;
}

1296
thirdparty/lws/client/client.c vendored Normal file

File diff suppressed because it is too large Load Diff

625
thirdparty/lws/client/ssl-client.c vendored Normal file
View File

@ -0,0 +1,625 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
extern int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
extern void
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
extern int lws_ssl_get_error(struct lws *wsi, int n);
#if defined(USE_WOLFSSL)
#else
static int
OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
#if defined(LWS_WITH_MBEDTLS)
lwsl_notice("%s\n", __func__);
return 0;
#else
SSL *ssl;
int n;
struct lws *wsi;
/* keep old behaviour accepting self-signed server certs */
if (!preverify_ok) {
int err = X509_STORE_CTX_get_error(x509_ctx);
if (err != X509_V_OK) {
ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) {
lwsl_notice("accepting self-signed certificate (verify_callback)\n");
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
return 1; // ok
} else if ((err == X509_V_ERR_CERT_NOT_YET_VALID ||
err == X509_V_ERR_CERT_HAS_EXPIRED) &&
wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) {
if (err == X509_V_ERR_CERT_NOT_YET_VALID)
lwsl_notice("accepting not yet valid certificate (verify_callback)\n");
else if (err == X509_V_ERR_CERT_HAS_EXPIRED)
lwsl_notice("accepting expired certificate (verify_callback)\n");
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
return 1; // ok
}
}
}
ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
n = lws_get_context_protocol(wsi->context, 0).callback(wsi,
LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION,
x509_ctx, ssl, preverify_ok);
/* keep old behaviour if something wrong with server certs */
/* if ssl error is overruled in callback and cert is ok,
* X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and
* return value is 0 from callback */
if (!preverify_ok) {
int err = X509_STORE_CTX_get_error(x509_ctx);
if (err != X509_V_OK) { /* cert validation error was not handled in callback */
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
const char* msg = X509_verify_cert_error_string(err);
lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth);
return preverify_ok; // not ok
}
}
/* convert callback return code from 0 = OK to verify callback return value 1 = OK */
return !n;
#endif
}
#endif
int
lws_ssl_client_bio_create(struct lws *wsi)
{
char hostname[128], *p;
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
_WSI_TOKEN_CLIENT_HOST) <= 0) {
lwsl_err("%s: Unable to get hostname\n", __func__);
return -1;
}
/*
* remove any :port part on the hostname... necessary for network
* connection but typical certificates do not contain it
*/
p = hostname;
while (*p) {
if (*p == ':') {
*p = '\0';
break;
}
p++;
}
wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
if (!wsi->ssl) {
lwsl_err("SSL_new failed: %s\n",
ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
lws_ssl_elaborate_error();
return -1;
}
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
#endif
#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host
X509_VERIFY_PARAM *param;
(void)param;
if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
param = SSL_get0_param(wsi->ssl);
/* Enable automatic hostname checks */
X509_VERIFY_PARAM_set_hostflags(param,
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
}
#endif
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
#ifndef USE_OLD_CYASSL
/* OpenSSL_client_verify_callback will be called @ SSL_connect() */
SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
#endif
#endif
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif
/*
* use server name indication (SNI), if supported,
* when establishing connection
*/
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
#ifdef CYASSL_SNI_HOST_NAME
CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname));
#endif
#else
#ifdef WOLFSSL_SNI_HOST_NAME
wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname));
#endif
#endif
#else
#if defined(LWS_WITH_MBEDTLS)
if (wsi->vhost->x509_client_CA)
SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
else
SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
#else
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
SSL_set_tlsext_host_name(wsi->ssl, hostname);
#endif
#endif
#endif
#ifdef USE_WOLFSSL
/*
* wolfSSL/CyaSSL does certificate verification differently
* from OpenSSL.
* If we should ignore the certificate, we need to set
* this before SSL_new and SSL_connect is called.
* Otherwise the connect will simply fail with error code -155
*/
#ifdef USE_OLD_CYASSL
if (wsi->use_ssl == 2)
CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
#else
if (wsi->use_ssl == 2)
wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
#endif
#endif /* USE_WOLFSSL */
#if !defined(LWS_WITH_MBEDTLS)
wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE);
SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
#else
SSL_set_fd(wsi->ssl, wsi->desc.sockfd);
#endif
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#else
#if !defined(LWS_WITH_MBEDTLS)
BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
#endif
#endif
#if !defined(LWS_WITH_MBEDTLS)
SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index,
wsi);
#endif
return 0;
}
#if defined(LWS_WITH_MBEDTLS)
int ERR_get_error(void)
{
return 0;
}
#endif
int
lws_ssl_client_connect1(struct lws *wsi)
{
struct lws_context *context = wsi->context;
int n = 0;
lws_latency_pre(context, wsi);
n = SSL_connect(wsi->ssl);
lws_latency(context, wsi,
"SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0);
if (n < 0) {
n = lws_ssl_get_error(wsi, n);
if (n == SSL_ERROR_WANT_READ)
goto some_wait;
if (n == SSL_ERROR_WANT_WRITE) {
/*
* wants us to retry connect due to
* state of the underlying ssl layer...
* but since it may be stalled on
* blocked write, no incoming data may
* arrive to trigger the retry.
* Force (possibly many times if the SSL
* state persists in returning the
* condition code, but other sockets
* are getting serviced inbetweentimes)
* us to get called back when writable.
*/
lwsl_info("%s: WANT_WRITE... retrying\n", __func__);
lws_callback_on_writable(wsi);
some_wait:
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
}
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
lwsl_err("ssl hs1 error, X509_V_ERR = %d: %s\n",
n, ERR_error_string(n, sb));
lws_ssl_elaborate_error();
}
n = -1;
}
if (n <= 0) {
/*
* retry if new data comes until we
* run into the connection timeout or win
*/
unsigned long error = ERR_get_error();
if (error != SSL_ERROR_NONE) {
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
lwsl_err("SSL connect error %lu: %s\n",
error, ERR_error_string(error, sb));
return -1;
}
return 0;
}
return 1;
}
int
lws_ssl_client_connect2(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
int n = 0;
if (wsi->mode == LWSCM_WSCL_WAITING_SSL) {
lws_latency_pre(context, wsi);
n = SSL_connect(wsi->ssl);
lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
lws_latency(context, wsi,
"SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0);
if (n < 0) {
n = lws_ssl_get_error(wsi, n);
if (n == SSL_ERROR_WANT_READ) {
lwsl_info("SSL_connect WANT_READ... retrying\n");
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
}
if (n == SSL_ERROR_WANT_WRITE) {
/*
* wants us to retry connect due to
* state of the underlying ssl layer...
* but since it may be stalled on
* blocked write, no incoming data may
* arrive to trigger the retry.
* Force (possibly many times if the SSL
* state persists in returning the
* condition code, but other sockets
* are getting serviced inbetweentimes)
* us to get called back when writable.
*/
lwsl_info("SSL_connect WANT_WRITE... retrying\n");
lws_callback_on_writable(wsi);
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
}
n = -1;
}
if (n <= 0) {
/*
* retry if new data comes until we
* run into the connection timeout or win
*/
unsigned long error = ERR_get_error();
if (error != SSL_ERROR_NONE) {
lwsl_err("SSL connect error %lu: %s\n",
error, ERR_error_string(error, sb));
return -1;
}
}
}
#if defined(LWS_WITH_MBEDTLS)
{
X509 *peer = SSL_get_peer_certificate(wsi->ssl);
if (!peer) {
lwsl_notice("peer did not provide cert\n");
return -1;
}
lwsl_notice("peer provided cert\n");
}
#endif
#ifndef USE_WOLFSSL
/*
* See comment above about wolfSSL certificate
* verification
*/
lws_latency_pre(context, wsi);
n = SSL_get_verify_result(wsi->ssl);
lws_latency(context, wsi,
"SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
lwsl_debug("get_verify says %d\n", n);
if (n != X509_V_OK) {
if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
(wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
lwsl_notice("accepting self-signed certificate\n");
} else if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
n == X509_V_ERR_CERT_HAS_EXPIRED) &&
(wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) {
lwsl_notice("accepting expired certificate\n");
} else if (n == X509_V_ERR_CERT_NOT_YET_VALID) {
lwsl_notice("Cert is from the future... "
"probably our clock... accepting...\n");
} else {
lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n",
n, ERR_error_string(n, sb));
lws_ssl_elaborate_error();
return -1;
}
}
#endif /* USE_WOLFSSL */
return 1;
}
int lws_context_init_client_ssl(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
SSL_METHOD *method = NULL;
struct lws wsi;
unsigned long error;
const char *ca_filepath = info->ssl_ca_filepath;
#if !defined(LWS_WITH_MBEDTLS)
const char *cipher_list = info->ssl_cipher_list;
const char *private_key_filepath = info->ssl_private_key_filepath;
const char *cert_filepath = info->ssl_cert_filepath;
int n;
if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW)
return 0;
/*
* for backwards-compatibility default to using ssl_... members, but
* if the newer client-specific ones are given, use those
*/
if (info->client_ssl_cipher_list)
cipher_list = info->client_ssl_cipher_list;
if (info->client_ssl_cert_filepath)
cert_filepath = info->client_ssl_cert_filepath;
if (info->client_ssl_private_key_filepath)
private_key_filepath = info->client_ssl_private_key_filepath;
#endif
if (info->client_ssl_ca_filepath)
ca_filepath = info->client_ssl_ca_filepath;
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return 0;
if (vhost->ssl_client_ctx)
return 0;
if (info->provided_client_ssl_ctx) {
/* use the provided OpenSSL context if given one */
vhost->ssl_client_ctx = info->provided_client_ssl_ctx;
/* nothing for lib to delete */
vhost->user_supplied_ssl_ctx = 1;
return 0;
}
/* basic openssl init already happened in context init */
/* choose the most recent spin of the api */
#if defined(LWS_HAVE_TLS_CLIENT_METHOD)
method = (SSL_METHOD *)TLS_client_method();
#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD)
method = (SSL_METHOD *)TLSv1_2_client_method();
#else
method = (SSL_METHOD *)SSLv23_client_method();
#endif
if (!method) {
error = ERR_get_error();
lwsl_err("problem creating ssl method %lu: %s\n",
error, ERR_error_string(error,
(char *)vhost->context->pt[0].serv_buf));
return 1;
}
/* create context */
vhost->ssl_client_ctx = SSL_CTX_new(method);
if (!vhost->ssl_client_ctx) {
error = ERR_get_error();
lwsl_err("problem creating ssl context %lu: %s\n",
error, ERR_error_string(error,
(char *)vhost->context->pt[0].serv_buf));
return 1;
}
lwsl_notice("created client ssl context for %s\n", vhost->name);
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION);
#endif
#if defined(LWS_WITH_MBEDTLS)
if (ca_filepath) {
lws_filepos_t len;
uint8_t *buf;
/*
* prototype this here, the shim does not export it in the
* header, and we need to use the shim unchanged for ESP32 case
*/
X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len);
if (alloc_file(vhost->context, ca_filepath, &buf, &len)) {
lwsl_err("Load CA cert file %s failed\n", ca_filepath);
return 1;
}
vhost->x509_client_CA = d2i_X509(NULL, buf, len);
free(buf);
if (!vhost->x509_client_CA) {
lwsl_err("client CA: x509 parse failed\n");
return 1;
}
SSL_CTX_add_client_CA(vhost->ssl_client_ctx,
vhost->x509_client_CA);
lwsl_notice("client loaded CA for verification %s\n", ca_filepath);
}
#else
SSL_CTX_set_options(vhost->ssl_client_ctx,
SSL_OP_CIPHER_SERVER_PREFERENCE);
if (cipher_list)
SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, cipher_list);
#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
/* loads OS default CA certs */
SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx);
#endif
/* openssl init for cert verification (for client sockets) */
if (!ca_filepath) {
if (!SSL_CTX_load_verify_locations(
vhost->ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS))
lwsl_err("Unable to load SSL Client certs from %s "
"(set by LWS_OPENSSL_CLIENT_CERTS) -- "
"client ssl isn't going to work\n",
LWS_OPENSSL_CLIENT_CERTS);
} else
if (!SSL_CTX_load_verify_locations(
vhost->ssl_client_ctx, ca_filepath, NULL)) {
lwsl_err(
"Unable to load SSL Client certs "
"file from %s -- client ssl isn't "
"going to work\n", info->client_ssl_ca_filepath);
lws_ssl_elaborate_error();
}
else
lwsl_info("loaded ssl_ca_filepath\n");
/*
* callback allowing user code to load extra verification certs
* helping the client to verify server identity
*/
/* support for client-side certificate authentication */
if (cert_filepath) {
lwsl_notice("%s: doing cert filepath\n", __func__);
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx,
cert_filepath);
if (n < 1) {
lwsl_err("problem %d getting cert '%s'\n", n,
cert_filepath);
lws_ssl_elaborate_error();
return 1;
}
lwsl_notice("Loaded client cert %s\n", cert_filepath);
}
if (private_key_filepath) {
lwsl_notice("%s: doing private key filepath\n", __func__);
lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info);
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx,
private_key_filepath, SSL_FILETYPE_PEM) != 1) {
lwsl_err("use_PrivateKey_file '%s'\n",
private_key_filepath);
lws_ssl_elaborate_error();
return 1;
}
lwsl_notice("Loaded client cert private key %s\n",
private_key_filepath);
/* verify private key */
if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) {
lwsl_err("Private SSL key doesn't match cert\n");
return 1;
}
}
#endif
/*
* give him a fake wsi with context set, so he can use
* lws_get_context() in the callback
*/
memset(&wsi, 0, sizeof(wsi));
wsi.vhost = vhost;
wsi.context = vhost->context;
vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
vhost->ssl_client_ctx, NULL, 0);
return 0;
}

1628
thirdparty/lws/context.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,473 @@
/*
* ./lib/extension-permessage-deflate.c
*
* Copyright (C) 2016 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include "extension-permessage-deflate.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define LWS_ZLIB_MEMLEVEL 8
const struct lws_ext_options lws_ext_pm_deflate_options[] = {
/* public RFC7692 settings */
{ "server_no_context_takeover", EXTARG_NONE },
{ "client_no_context_takeover", EXTARG_NONE },
{ "server_max_window_bits", EXTARG_OPT_DEC },
{ "client_max_window_bits", EXTARG_OPT_DEC },
/* ones only user code can set */
{ "rx_buf_size", EXTARG_DEC },
{ "tx_buf_size", EXTARG_DEC },
{ "compression_level", EXTARG_DEC },
{ "mem_level", EXTARG_DEC },
{ NULL, 0 }, /* sentinel */
};
static void
lws_extension_pmdeflate_restrict_args(struct lws *wsi,
struct lws_ext_pm_deflate_priv *priv)
{
int n, extra;
/* cap the RX buf at the nearest power of 2 to protocol rx buf */
n = wsi->context->pt_serv_buf_size;
if (wsi->protocol->rx_buffer_size)
n = wsi->protocol->rx_buffer_size;
extra = 7;
while (n >= 1 << (extra + 1))
extra++;
if (extra < priv->args[PMD_RX_BUF_PWR2]) {
priv->args[PMD_RX_BUF_PWR2] = extra;
lwsl_info(" Capping pmd rx to %d\n", 1 << extra);
}
}
LWS_VISIBLE int
lws_extension_callback_pm_deflate(struct lws_context *context,
const struct lws_extension *ext,
struct lws *wsi,
enum lws_extension_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_ext_pm_deflate_priv *priv =
(struct lws_ext_pm_deflate_priv *)user;
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
static unsigned char trail[] = { 0, 0, 0xff, 0xff };
int n, ret = 0, was_fin = 0, extra;
struct lws_ext_option_arg *oa;
switch (reason) {
case LWS_EXT_CB_NAMED_OPTION_SET:
oa = in;
if (!oa->option_name)
break;
for (n = 0; n < ARRAY_SIZE(lws_ext_pm_deflate_options); n++)
if (!strcmp(lws_ext_pm_deflate_options[n].name, oa->option_name))
break;
if (n == ARRAY_SIZE(lws_ext_pm_deflate_options))
break;
oa->option_index = n;
/* fallthru */
case LWS_EXT_CB_OPTION_SET:
oa = in;
lwsl_notice("%s: option set: idx %d, %s, len %d\n", __func__,
oa->option_index, oa->start, oa->len);
if (oa->start)
priv->args[oa->option_index] = atoi(oa->start);
else
priv->args[oa->option_index] = 1;
if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
lws_extension_pmdeflate_restrict_args(wsi, priv);
break;
case LWS_EXT_CB_OPTION_CONFIRM:
if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
return -1;
break;
case LWS_EXT_CB_CLIENT_CONSTRUCT:
case LWS_EXT_CB_CONSTRUCT:
n = context->pt_serv_buf_size;
if (wsi->protocol->rx_buffer_size)
n = wsi->protocol->rx_buffer_size;
if (n < 128) {
lwsl_info(" permessage-deflate requires the protocol (%s) to have an RX buffer >= 128\n",
wsi->protocol->name);
return -1;
}
/* fill in **user */
priv = lws_zalloc(sizeof(*priv), "pmd priv");
*((void **)user) = priv;
lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__);
memset(priv, 0, sizeof(*priv));
/* fill in pointer to options list */
if (in)
*((const struct lws_ext_options **)in) =
lws_ext_pm_deflate_options;
/* fallthru */
case LWS_EXT_CB_OPTION_DEFAULT:
/* set the public, RFC7692 defaults... */
priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
/* ...and the ones the user code can override */
priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
priv->args[PMD_COMP_LEVEL] = 1;
priv->args[PMD_MEM_LEVEL] = 8;
lws_extension_pmdeflate_restrict_args(wsi, priv);
break;
case LWS_EXT_CB_DESTROY:
lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__);
lws_free(priv->buf_rx_inflated);
lws_free(priv->buf_tx_deflated);
if (priv->rx_init)
(void)inflateEnd(&priv->rx);
if (priv->tx_init)
(void)deflateEnd(&priv->tx);
lws_free(priv);
return ret;
case LWS_EXT_CB_PAYLOAD_RX:
lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n",
__func__, eff_buf->token_len, priv->rx.avail_in);
if (!(wsi->u.ws.rsv_first_msg & 0x40))
return 0;
#if 0
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
printf("\n");
}
printf("\n");
#endif
if (!priv->rx_init)
if (inflateInit2(&priv->rx, -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
lwsl_err("%s: iniflateInit failed\n", __func__);
return -1;
}
priv->rx_init = 1;
if (!priv->buf_rx_inflated)
priv->buf_rx_inflated = lws_malloc(LWS_PRE + 7 + 5 +
(1 << priv->args[PMD_RX_BUF_PWR2]), "pmd rx inflate buf");
if (!priv->buf_rx_inflated) {
lwsl_err("%s: OOM\n", __func__);
return -1;
}
/*
* We have to leave the input stream alone if we didn't
* finish with it yet. The input stream is held in the wsi
* rx buffer by the caller, so this assumption is safe while
* we block new rx while draining the existing rx
*/
if (!priv->rx.avail_in && eff_buf->token && eff_buf->token_len) {
priv->rx.next_in = (unsigned char *)eff_buf->token;
priv->rx.avail_in = eff_buf->token_len;
}
priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
eff_buf->token = (char *)priv->rx.next_out;
priv->rx.avail_out = 1 << priv->args[PMD_RX_BUF_PWR2];
if (priv->rx_held_valid) {
lwsl_ext("-- RX piling on held byte --\n");
*(priv->rx.next_out++) = priv->rx_held;
priv->rx.avail_out--;
priv->rx_held_valid = 0;
}
/* if...
*
* - he has no remaining input content for this message, and
* - and this is the final fragment, and
* - we used everything that could be drained on the input side
*
* ...then put back the 00 00 FF FF the sender stripped as our
* input to zlib
*/
if (!priv->rx.avail_in && wsi->u.ws.final &&
!wsi->u.ws.rx_packet_length) {
lwsl_ext("RX APPEND_TRAILER-DO\n");
was_fin = 1;
priv->rx.next_in = trail;
priv->rx.avail_in = sizeof(trail);
}
n = inflate(&priv->rx, Z_NO_FLUSH);
lwsl_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", n,
priv->rx.avail_in, priv->rx.avail_out, wsi->u.ws.final);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
lwsl_info("zlib error inflate %d: %s\n",
n, priv->rx.msg);
return -1;
}
/*
* If we did not already send in the 00 00 FF FF, and he's
* out of input, he did not EXACTLY fill the output buffer
* (which is ambiguous and we will force it to go around
* again by withholding a byte), and he's otherwise working on
* being a FIN fragment, then do the FIN message processing
* of faking up the 00 00 FF FF that the sender stripped.
*/
if (!priv->rx.avail_in && wsi->u.ws.final &&
!wsi->u.ws.rx_packet_length && !was_fin &&
priv->rx.avail_out /* ambiguous as to if it is the end */
) {
lwsl_ext("RX APPEND_TRAILER-DO\n");
was_fin = 1;
priv->rx.next_in = trail;
priv->rx.avail_in = sizeof(trail);
n = inflate(&priv->rx, Z_SYNC_FLUSH);
lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", n,
priv->rx.avail_in, priv->rx.avail_out);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
lwsl_info("zlib error inflate %d: %s\n",
n, priv->rx.msg);
return -1;
}
}
/*
* we must announce in our returncode now if there is more
* output to be expected from inflate, so we can decide to
* set the FIN bit on this bufferload or not. However zlib
* is ambiguous when we exactly filled the inflate buffer. It
* does not give us a clue as to whether we should understand
* that to mean he ended on a buffer boundary, or if there is
* more in the pipeline.
*
* So to work around that safely, if it used all output space
* exactly, we ALWAYS say there is more coming and we withhold
* the last byte of the buffer to guarantee that is true.
*
* That still leaves us at least one byte to finish with a FIN
* on, even if actually nothing more is coming from the next
* inflate action itself.
*/
if (!priv->rx.avail_out) { /* he used all available out buf */
lwsl_ext("-- rx grabbing held --\n");
/* snip the last byte and hold it for next time */
priv->rx_held = *(--priv->rx.next_out);
priv->rx_held_valid = 1;
}
eff_buf->token_len = (char *)priv->rx.next_out - eff_buf->token;
priv->count_rx_between_fin += eff_buf->token_len;
lwsl_ext(" %s: RX leaving with new effbuff len %d, "
"ret %d, rx.avail_in=%d, TOTAL RX since FIN %lu\n",
__func__, eff_buf->token_len, priv->rx_held_valid,
priv->rx.avail_in,
(unsigned long)priv->count_rx_between_fin);
if (was_fin) {
priv->count_rx_between_fin = 0;
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
(void)inflateEnd(&priv->rx);
priv->rx_init = 0;
}
}
#if 0
for (n = 0; n < eff_buf->token_len; n++)
putchar(eff_buf->token[n]);
puts("\n");
#endif
return priv->rx_held_valid;
case LWS_EXT_CB_PAYLOAD_TX:
if (!priv->tx_init) {
n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
Z_DEFLATED,
-priv->args[PMD_SERVER_MAX_WINDOW_BITS +
(wsi->vhost->listen_port <= 0)],
priv->args[PMD_MEM_LEVEL],
Z_DEFAULT_STRATEGY);
if (n != Z_OK) {
lwsl_ext("inflateInit2 failed %d\n", n);
return 1;
}
}
priv->tx_init = 1;
if (!priv->buf_tx_deflated)
priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 +
(1 << priv->args[PMD_TX_BUF_PWR2]), "pmd tx deflate buf");
if (!priv->buf_tx_deflated) {
lwsl_err("%s: OOM\n", __func__);
return -1;
}
if (eff_buf->token) {
lwsl_ext("%s: TX: eff_buf length %d\n", __func__,
eff_buf->token_len);
priv->tx.next_in = (unsigned char *)eff_buf->token;
priv->tx.avail_in = eff_buf->token_len;
}
#if 0
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
printf("\n");
}
printf("\n");
#endif
priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
eff_buf->token = (char *)priv->tx.next_out;
priv->tx.avail_out = 1 << priv->args[PMD_TX_BUF_PWR2];
n = deflate(&priv->tx, Z_SYNC_FLUSH);
if (n == Z_STREAM_ERROR) {
lwsl_ext("%s: Z_STREAM_ERROR\n", __func__);
return -1;
}
if (priv->tx_held_valid) {
priv->tx_held_valid = 0;
if (priv->tx.avail_out == 1 << priv->args[PMD_TX_BUF_PWR2])
/*
* we can get a situation he took something in
* but did not generate anything out, at the end
* of a message (eg, next thing he sends is 80
* 00, a zero length FIN, like Authobahn can
* send).
* If we have come back as a FIN, we must not
* place the pending trailer 00 00 FF FF, just
* the 1 byte of live data
*/
*(--eff_buf->token) = priv->tx_held[0];
else {
/* he generated data, prepend whole pending */
eff_buf->token -= 5;
for (n = 0; n < 5; n++)
eff_buf->token[n] = priv->tx_held[n];
}
}
priv->compressed_out = 1;
eff_buf->token_len = (int)(priv->tx.next_out -
(unsigned char *)eff_buf->token);
/*
* we must announce in our returncode now if there is more
* output to be expected from inflate, so we can decide to
* set the FIN bit on this bufferload or not. However zlib
* is ambiguous when we exactly filled the inflate buffer. It
* does not give us a clue as to whether we should understand
* that to mean he ended on a buffer boundary, or if there is
* more in the pipeline.
*
* Worse, the guy providing the stuff we are sending may not
* know until after that this was, actually, the last chunk,
* that can happen even if we did not fill the output buf, ie
* he may send after this a zero-length FIN fragment.
*
* This is super difficult because we must snip the last 4
* bytes in the case this is the last compressed output of the
* message. The only way to deal with it is defer sending the
* last 5 bytes of each frame until the next one, when we will
* be in a position to understand if that has a FIN or not.
*/
extra = !!(len & LWS_WRITE_NO_FIN) || !priv->tx.avail_out;
if (eff_buf->token_len >= 4 + extra) {
lwsl_ext("tx held %d\n", 4 + extra);
priv->tx_held_valid = extra;
for (n = 3 + extra; n >= 0; n--)
priv->tx_held[n] = *(--priv->tx.next_out);
eff_buf->token_len -= 4 + extra;
}
lwsl_ext(" TX rewritten with new effbuff len %d, ret %d\n",
eff_buf->token_len, !priv->tx.avail_out);
return !priv->tx.avail_out; /* 1 == have more tx pending */
case LWS_EXT_CB_PACKET_TX_PRESEND:
if (!priv->compressed_out)
break;
priv->compressed_out = 0;
if ((*(eff_buf->token) & 0x80) &&
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n");
(void)deflateEnd(&priv->tx);
priv->tx_init = 0;
}
n = *(eff_buf->token) & 15;
/* set RSV1, but not on CONTINUATION */
if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
*eff_buf->token |= 0x40;
#if 0
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
puts("\n");
}
puts("\n");
#endif
lwsl_ext("%s: tx opcode 0x%02X\n", __func__,
(unsigned char)*eff_buf->token);
break;
default:
break;
}
return 0;
}

View File

@ -0,0 +1,41 @@
#include <zlib.h>
#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
enum arg_indexes {
PMD_SERVER_NO_CONTEXT_TAKEOVER,
PMD_CLIENT_NO_CONTEXT_TAKEOVER,
PMD_SERVER_MAX_WINDOW_BITS,
PMD_CLIENT_MAX_WINDOW_BITS,
PMD_RX_BUF_PWR2,
PMD_TX_BUF_PWR2,
PMD_COMP_LEVEL,
PMD_MEM_LEVEL,
PMD_ARG_COUNT
};
struct lws_ext_pm_deflate_priv {
z_stream rx;
z_stream tx;
unsigned char *buf_rx_inflated; /* RX inflated output buffer */
unsigned char *buf_tx_deflated; /* TX deflated output buffer */
size_t count_rx_between_fin;
unsigned char args[PMD_ARG_COUNT];
unsigned char tx_held[5];
unsigned char rx_held;
unsigned char tx_init:1;
unsigned char rx_init:1;
unsigned char compressed_out:1;
unsigned char rx_held_valid:1;
unsigned char tx_held_valid:1;
unsigned char rx_append_trailer:1;
unsigned char pending_tx_trailer:1;
};

344
thirdparty/lws/ext/extension.c vendored Normal file
View File

@ -0,0 +1,344 @@
#include "private-libwebsockets.h"
#include "extension-permessage-deflate.h"
LWS_VISIBLE void
lws_context_init_extensions(struct lws_context_creation_info *info,
struct lws_context *context)
{
lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
}
enum lws_ext_option_parser_states {
LEAPS_SEEK_NAME,
LEAPS_EAT_NAME,
LEAPS_SEEK_VAL,
LEAPS_EAT_DEC,
LEAPS_SEEK_ARG_TERM
};
LWS_VISIBLE int
lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
void *ext_user, const struct lws_ext_options *opts,
const char *in, int len)
{
enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
unsigned int match_map = 0, n, m, w = 0, count_options = 0,
pending_close_quote = 0;
struct lws_ext_option_arg oa;
oa.option_name = NULL;
while (opts[count_options].name)
count_options++;
while (len) {
lwsl_ext("'%c' %d", *in, leap);
switch (leap) {
case LEAPS_SEEK_NAME:
if (*in == ' ')
break;
if (*in == ',') {
len = 1;
break;
}
match_map = (1 << count_options) - 1;
leap = LEAPS_EAT_NAME;
w = 0;
/* fallthru */
case LEAPS_EAT_NAME:
oa.start = NULL;
oa.len = 0;
m = match_map;
n = 0;
pending_close_quote = 0;
while (m) {
if (m & 1) {
lwsl_ext(" m=%d, n=%d, w=%d\n", m, n, w);
if (*in == opts[n].name[w]) {
if (!opts[n].name[w + 1]) {
oa.option_index = n;
lwsl_ext("hit %d\n", oa.option_index);
leap = LEAPS_SEEK_VAL;
if (len == 1)
goto set_arg;
break;
}
} else {
match_map &= ~(1 << n);
if (!match_map) {
lwsl_ext("empty match map\n");
return -1;
}
}
}
m >>= 1;
n++;
}
w++;
break;
case LEAPS_SEEK_VAL:
if (*in == ' ')
break;
if (*in == ',') {
len = 1;
break;
}
if (*in == ';' || len == 1) { /* ie,nonoptional */
if (opts[oa.option_index].type == EXTARG_DEC)
return -1;
leap = LEAPS_SEEK_NAME;
goto set_arg;
}
if (*in == '=') {
w = 0;
pending_close_quote = 0;
if (opts[oa.option_index].type == EXTARG_NONE)
return -1;
leap = LEAPS_EAT_DEC;
break;
}
return -1;
case LEAPS_EAT_DEC:
if (*in >= '0' && *in <= '9') {
if (!w)
oa.start = in;
w++;
if (len != 1)
break;
}
if (!w && *in =='"') {
pending_close_quote = 1;
break;
}
if (!w)
return -1;
if (pending_close_quote && *in != '"' && len != 1)
return -1;
leap = LEAPS_SEEK_ARG_TERM;
if (oa.start)
oa.len = in - oa.start;
if (len == 1)
oa.len++;
set_arg:
ext->callback(lws_get_context(wsi),
ext, wsi, LWS_EXT_CB_OPTION_SET,
ext_user, (char *)&oa, 0);
if (len == 1)
break;
if (pending_close_quote && *in == '"')
break;
/* fallthru */
case LEAPS_SEEK_ARG_TERM:
if (*in == ' ')
break;
if (*in == ';') {
leap = LEAPS_SEEK_NAME;
break;
}
if (*in == ',') {
len = 1;
break;
}
return -1;
}
len--;
in++;
}
return 0;
}
/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
{
int n, m, handled = 0;
for (n = 0; n < wsi->count_act_ext; n++) {
m = wsi->active_extensions[n]->callback(lws_get_context(wsi),
wsi->active_extensions[n], wsi, reason,
wsi->act_ext_user[n], arg, len);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
return -1;
}
/* valgrind... */
if (reason == LWS_EXT_CB_DESTROY)
wsi->act_ext_user[n] = NULL;
if (m > handled)
handled = m;
}
return handled;
}
int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
int reason, void *arg, int len)
{
int n = 0, m, handled = 0;
const struct lws_extension *ext;
if (!wsi || !wsi->vhost)
return 0;
ext = wsi->vhost->extensions;
while (ext && ext->callback && !handled) {
m = ext->callback(context, ext, wsi, reason,
(void *)(lws_intptr_t)n, arg, len);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
return -1;
}
if (m)
handled = 1;
ext++;
n++;
}
return 0;
}
int
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_tokens eff_buf;
int ret, m, n = 0;
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
/*
* while we have original buf to spill ourselves, or extensions report
* more in their pipeline
*/
ret = 1;
while (ret == 1) {
/* default to nobody has more to spill */
ret = 0;
/* show every extension the new incoming data */
m = lws_ext_cb_active(wsi,
LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
if (m < 0)
return -1;
if (m) /* handled */
ret = 1;
if ((char *)buf != eff_buf.token)
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->u.ws.clean_buffer = 0;
/* assuming they left us something to send, send it */
if (eff_buf.token_len) {
n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
eff_buf.token_len);
if (n < 0) {
lwsl_info("closing from ext access\n");
return -1;
}
/* always either sent it all or privately buffered */
if (wsi->u.ws.clean_buffer)
len = n;
}
lwsl_parser("written %d bytes to client\n", n);
/* no extension has more to spill? Then we can go */
if (!ret)
break;
/* we used up what we had */
eff_buf.token = NULL;
eff_buf.token_len = 0;
/*
* Did that leave the pipe choked?
* Or we had to hold on to some of it?
*/
if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len)
/* no we could add more, lets's do that */
continue;
lwsl_debug("choked\n");
/*
* Yes, he's choked. Don't spill the rest now get a callback
* when he is ready to send and take care of it there
*/
lws_callback_on_writable(wsi);
wsi->extension_data_pending = 1;
ret = 0;
}
return len;
}
int
lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
void *v, size_t len)
{
struct lws_context *context = wsi->context;
int n, handled = 0;
/* maybe an extension will take care of it for us */
for (n = 0; n < wsi->count_act_ext && !handled; n++) {
if (!wsi->active_extensions[n]->callback)
continue;
handled |= wsi->active_extensions[n]->callback(context,
wsi->active_extensions[n], wsi,
r, wsi->act_ext_user[n], v, len);
}
return handled;
}
int
lws_set_extension_option(struct lws *wsi, const char *ext_name,
const char *opt_name, const char *opt_val)
{
struct lws_ext_option_arg oa;
int idx = 0;
/* first identify if the ext is active on this wsi */
while (idx < wsi->count_act_ext &&
strcmp(wsi->active_extensions[idx]->name, ext_name))
idx++;
if (idx == wsi->count_act_ext)
return -1; /* request ext not active on this wsi */
oa.option_name = opt_name;
oa.option_index = 0;
oa.start = opt_val;
oa.len = 0;
return wsi->active_extensions[idx]->callback(
wsi->context, wsi->active_extensions[idx], wsi,
LWS_EXT_CB_NAMED_OPTION_SET, wsi->act_ext_user[idx], &oa, 0);
}

280
thirdparty/lws/handshake.c vendored Normal file
View File

@ -0,0 +1,280 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
/*
* -04 of the protocol (actually the 80th version) has a radically different
* handshake. The 04 spec gives the following idea
*
* The handshake from the client looks as follows:
*
* GET /chat HTTP/1.1
* Host: server.example.com
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
* Sec-WebSocket-Origin: http://example.com
* Sec-WebSocket-Protocol: chat, superchat
* Sec-WebSocket-Version: 4
*
* The handshake from the server looks as follows:
*
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
* Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
* Sec-WebSocket-Protocol: chat
*/
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/*
* We have to take care about parsing because the headers may be split
* into multiple fragments. They may contain unknown headers with arbitrary
* argument lengths. So, we parse using a single-character at a time state
* machine that is completely independent of packet size.
*
* Returns <0 for error or length of chars consumed from buf (up to len)
*/
LWS_VISIBLE int
lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
{
unsigned char *last_char, *oldbuf = buf;
lws_filepos_t body_chunk_len;
size_t n;
switch (wsi->state) {
#ifdef LWS_WITH_HTTP2
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
case LWSS_HTTP2_ESTABLISHED:
n = 0;
//lwsl_debug("%s: starting new block of %d\n", __func__, (int)len);
/*
* wsi here is always the network connection wsi, not a stream
* wsi.
*/
while (n < len) {
/*
* we were accepting input but now we stopped doing so
*/
if (lws_is_flowcontrolled(wsi)) {
lws_rxflow_cache(wsi, buf, n, len);
return 1;
}
/* account for what we're using in rxflow buffer */
if (wsi->rxflow_buffer) {
wsi->rxflow_pos++;
assert(wsi->rxflow_pos <= wsi->rxflow_len);
}
if (lws_h2_parser(wsi, buf[n++])) {
lwsl_debug("%s: http2_parser bailed\n", __func__);
goto bail;
}
}
lwsl_debug("%s: used up block of %d\n", __func__, (int)len);
break;
#endif
case LWSS_HTTP_ISSUING_FILE:
return 0;
case LWSS_CLIENT_HTTP_ESTABLISHED:
break;
case LWSS_HTTP:
wsi->hdr_parsing_completed = 0;
/* fallthru */
case LWSS_HTTP_HEADERS:
if (!wsi->u.hdr.ah) {
lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__);
assert(0);
}
lwsl_parser("issuing %d bytes to parser\n", (int)len);
lwsl_hexdump(buf, (size_t)len);
if (lws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
last_char = buf;
if (lws_handshake_server(wsi, &buf, (size_t)len))
/* Handshake indicates this session is done. */
goto bail;
/* we might have transitioned to RAW */
if (wsi->mode == LWSCM_RAW)
/* we gave the read buffer to RAW handler already */
goto read_ok;
/*
* It's possible that we've exhausted our data already, or
* rx flow control has stopped us dealing with this early,
* but lws_handshake_server doesn't update len for us.
* Figure out how much was read, so that we can proceed
* appropriately:
*/
len -= (buf - last_char);
lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
if (!wsi->hdr_parsing_completed)
/* More header content on the way */
goto read_ok;
switch (wsi->state) {
case LWSS_HTTP:
case LWSS_HTTP_HEADERS:
goto read_ok;
case LWSS_HTTP_ISSUING_FILE:
goto read_ok;
case LWSS_HTTP_BODY:
wsi->u.http.rx_content_remain =
wsi->u.http.rx_content_length;
if (wsi->u.http.rx_content_remain)
goto http_postbody;
/* there is no POST content */
goto postbody_completion;
default:
break;
}
break;
case LWSS_HTTP_BODY:
http_postbody:
//lwsl_notice("http post body\n");
while (len && wsi->u.http.rx_content_remain) {
/* Copy as much as possible, up to the limit of:
* what we have in the read buffer (len)
* remaining portion of the POST body (content_remain)
*/
body_chunk_len = min(wsi->u.http.rx_content_remain, len);
wsi->u.http.rx_content_remain -= body_chunk_len;
len -= body_chunk_len;
#ifdef LWS_WITH_CGI
if (wsi->cgi) {
struct lws_cgi_args args;
args.ch = LWS_STDIN;
args.stdwsi = &wsi->cgi->stdwsi[0];
args.data = buf;
args.len = body_chunk_len;
/* returns how much used */
n = user_callback_handle_rxflow(
wsi->protocol->callback,
wsi, LWS_CALLBACK_CGI_STDIN_DATA,
wsi->user_space,
(void *)&args, 0);
if ((int)n < 0)
goto bail;
} else {
#endif
n = wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY, wsi->user_space,
buf, (size_t)body_chunk_len);
if (n)
goto bail;
n = (size_t)body_chunk_len;
#ifdef LWS_WITH_CGI
}
#endif
buf += n;
if (wsi->u.http.rx_content_remain) {
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
wsi->context->timeout_secs);
break;
}
/* he sent all the content in time */
postbody_completion:
#ifdef LWS_WITH_CGI
/*
* If we're running a cgi, we can't let him off the
* hook just because he sent his POST data
*/
if (wsi->cgi)
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI,
wsi->context->timeout_secs);
else
#endif
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
#ifdef LWS_WITH_CGI
if (!wsi->cgi)
#endif
{
lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
n = wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY_COMPLETION,
wsi->user_space, NULL, 0);
if (n)
goto bail;
if (wsi->http2_substream)
wsi->state = LWSS_HTTP2_ESTABLISHED;
}
break;
}
break;
case LWSS_ESTABLISHED:
case LWSS_AWAITING_CLOSE_ACK:
case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
case LWSS_SHUTDOWN:
if (lws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
switch (wsi->mode) {
case LWSCM_WS_SERVING:
if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
lwsl_info("interpret_incoming_packet has bailed\n");
goto bail;
}
break;
}
break;
default:
lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
break;
}
read_ok:
/* Nothing more to do for now */
lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
return buf - oldbuf;
bail:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
return -1;
}

355
thirdparty/lws/header.c vendored Normal file
View File

@ -0,0 +1,355 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include "lextable-strings.h"
const unsigned char *lws_token_to_string(enum lws_token_indexes token)
{
if ((unsigned int)token >= ARRAY_SIZE(set))
return NULL;
return (unsigned char *)set[token];
}
int
lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
{
#ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return lws_add_http2_header_by_name(wsi, name,
value, length, p, end);
#else
(void)wsi;
#endif
if (name) {
while (*p < end && *name)
*((*p)++) = *name++;
if (*p == end)
return 1;
*((*p)++) = ' ';
}
if (*p + length + 3 >= end)
return 1;
memcpy(*p, value, length);
*p += length;
*((*p)++) = '\x0d';
*((*p)++) = '\x0a';
return 0;
}
int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
unsigned char *end)
{
#ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return 0;
#else
(void)wsi;
#endif
if ((lws_intptr_t)(end - *p) < 3)
return 1;
*((*p)++) = '\x0d';
*((*p)++) = '\x0a';
return 0;
}
int
lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
{
const unsigned char *name;
#ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return lws_add_http2_header_by_token(wsi, token, value,
length, p, end);
#endif
name = lws_token_to_string(token);
if (!name)
return 1;
return lws_add_http_header_by_name(wsi, name, value, length, p, end);
}
int lws_add_http_header_content_length(struct lws *wsi,
lws_filepos_t content_length,
unsigned char **p, unsigned char *end)
{
char b[24];
int n;
n = sprintf(b, "%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->u.http.tx_content_length = content_length;
wsi->u.http.tx_content_remain = content_length;
return 0;
}
STORE_IN_ROM static const char * const err400[] = {
"Bad Request",
"Unauthorized",
"Payment Required",
"Forbidden",
"Not Found",
"Method Not Allowed",
"Not Acceptable",
"Proxy Auth Required",
"Request Timeout",
"Conflict",
"Gone",
"Length Required",
"Precondition Failed",
"Request Entity Too Large",
"Request URI too Long",
"Unsupported Media Type",
"Requested Range Not Satisfiable",
"Expectation Failed"
};
STORE_IN_ROM static const char * const err500[] = {
"Internal Server Error",
"Not Implemented",
"Bad Gateway",
"Service Unavailable",
"Gateway Timeout",
"HTTP Version Not Supported"
};
int
lws_add_http_header_status(struct lws *wsi, unsigned int _code,
unsigned char **p, unsigned char *end)
{
STORE_IN_ROM 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;
#ifdef LWS_WITH_ACCESS_LOG
wsi->access_log.response = code;
#endif
#ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return lws_add_http2_header_status(wsi, code, p, end);
#endif
if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
description = err400[code - 400];
if (code >= 500 && code < (500 + ARRAY_SIZE(err500)))
description = err500[code - 500];
if (code == 100)
description = "Continue";
if (code == 200)
description = "OK";
if (code == 304)
description = "Not Modified";
else
if (code >= 300 && code < 400)
description = "Redirect";
if (wsi->u.http.request_version < ARRAY_SIZE(hver))
p1 = hver[wsi->u.http.request_version];
else
p1 = hver[0];
n = sprintf((char *)code_and_desc, "%s %u %s", p1, code, description);
if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p, end))
return 1;
headers = wsi->vhost->headers;
while (headers) {
if (lws_add_http_header_by_name(wsi,
(const unsigned char *)headers->name,
(unsigned char *)headers->value,
strlen(headers->value), p, end))
return 1;
headers = headers->next;
}
if (wsi->context->server_string &&
!(_code & LWSAHH_FLAG_NO_SERVER_NAME))
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
(unsigned char *)wsi->context->server_string,
wsi->context->server_string_len, p, end))
return 1;
if (wsi->vhost->options & LWS_SERVER_OPTION_STS)
if (lws_add_http_header_by_name(wsi, (unsigned char *)
"Strict-Transport-Security:",
(unsigned char *)"max-age=15768000 ; "
"includeSubDomains", 36, p, end))
return 1;
return 0;
}
LWS_VISIBLE int
lws_return_http_status(struct lws *wsi, unsigned int code,
const char *html_body)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
unsigned char *p = pt->serv_buf + LWS_PRE;
unsigned char *start = p;
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
int n = 0, m = 0, len;
char slen[20];
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 = 35 + strlen(html_body) + sprintf(slen, "%d", code);
n = sprintf(slen, "%d", len);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)slen, n,
&p, end))
return 1;
if (lws_finalize_http_header(wsi, &p, end))
return 1;
#if defined(LWS_WITH_HTTP2)
if (wsi->http2_substream) {
unsigned char *body = p + 512;
/*
* 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, p - start, LWS_WRITE_HTTP_HEADERS);
if (m != (int)(p - start))
return 1;
/*
* ... but stash the body and send it as a priority next
* handle_POLLOUT
*/
len = sprintf((char *)body,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
wsi->u.http.tx_content_length = len;
wsi->u.http.tx_content_remain = len;
wsi->u.h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
"pending status body");
if (!wsi->u.h2.pending_status_body)
return -1;
strcpy(wsi->u.h2.pending_status_body + LWS_PRE,
(const char *)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.
*/
p += lws_snprintf((char *)p, end - p - 1,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
n = (int)(p - start);
m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
if (m != n)
return 1;
}
lwsl_notice("%s: return\n", __func__);
return m != n;
}
LWS_VISIBLE int
lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
unsigned char **p, unsigned char *end)
{
unsigned char *start = *p;
int n;
if (lws_add_http_header_status(wsi, code, p, end))
return -1;
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_LOCATION,
loc, len, p, end))
return -1;
/*
* if we're going with http/1.1 and keepalive, we have to give fake
* content metadata so the client knows we completed the transaction and
* it can do the redirect...
*/
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)"text/html", 9,
p, end))
return -1;
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)"0", 1, p, end))
return -1;
if (lws_finalize_http_header(wsi, p, end))
return -1;
n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
return n;
}

104
thirdparty/lws/lextable-strings.h vendored Normal file
View File

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

805
thirdparty/lws/lextable.h vendored Normal file
View File

@ -0,0 +1,805 @@
/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */,
0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */,
0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */,
0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */,
0x63 /* 'c' */, 0x69, 0x00 /* (to 0x0075 state 23) */,
0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0099 state 34) */,
0x73 /* 's' */, 0xA0, 0x00 /* (to 0x00B2 state 48) */,
0x0D /* '.' */, 0xD9, 0x00 /* (to 0x00EE state 68) */,
0x61 /* 'a' */, 0x31, 0x01 /* (to 0x0149 state 129) */,
0x69 /* 'i' */, 0x70, 0x01 /* (to 0x018B state 163) */,
0x64 /* 'd' */, 0x19, 0x02 /* (to 0x0237 state 265) */,
0x72 /* 'r' */, 0x22, 0x02 /* (to 0x0243 state 270) */,
0x3A /* ':' */, 0x53, 0x02 /* (to 0x0277 state 299) */,
0x65 /* 'e' */, 0xDF, 0x02 /* (to 0x0306 state 409) */,
0x66 /* 'f' */, 0xFB, 0x02 /* (to 0x0325 state 425) */,
0x6C /* 'l' */, 0x1D, 0x03 /* (to 0x034A state 458) */,
0x6D /* 'm' */, 0x40, 0x03 /* (to 0x0370 state 484) */,
0x74 /* 't' */, 0xAF, 0x03 /* (to 0x03E2 state 578) */,
0x76 /* 'v' */, 0xD0, 0x03 /* (to 0x0406 state 606) */,
0x77 /* 'w' */, 0xDD, 0x03 /* (to 0x0416 state 614) */,
0x78 /* 'x' */, 0x04, 0x04 /* (to 0x0440 state 650) */,
0x08, /* fail */
/* pos 0040: 1 */ 0xE5 /* 'e' -> */,
/* pos 0041: 2 */ 0xF4 /* 't' -> */,
/* pos 0042: 3 */ 0xA0 /* ' ' -> */,
/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */,
/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */,
0x72 /* 'r' */, 0x95, 0x01 /* (to 0x01DD state 211) */,
0x61 /* 'a' */, 0xDD, 0x03 /* (to 0x0428 state 631) */,
0x75 /* 'u' */, 0xDF, 0x03 /* (to 0x042D state 635) */,
0x08, /* fail */
/* pos 0052: 6 */ 0xF3 /* 's' -> */,
/* pos 0053: 7 */ 0xF4 /* 't' -> */,
/* pos 0054: 8 */ 0xA0 /* ' ' -> */,
/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */,
/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */,
0x72 /* 'r' */, 0x51, 0x00 /* (to 0x00AB state 42) */,
0x08, /* fail */
/* pos 005e: 11 */ 0xF4 /* 't' -> */,
/* pos 005f: 12 */ 0xE9 /* 'i' -> */,
/* pos 0060: 13 */ 0xEF /* 'o' -> */,
/* pos 0061: 14 */ 0xEE /* 'n' -> */,
/* pos 0062: 15 */ 0xF3 /* 's' -> */,
/* pos 0063: 16 */ 0xA0 /* ' ' -> */,
/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */,
/* pos 0066: 18 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0070 state 19) */,
0x74 /* 't' */, 0xBF, 0x00 /* (to 0x0128 state 110) */,
0x65 /* 'e' */, 0xF8, 0x03 /* (to 0x0464 state 676) */,
0x08, /* fail */
/* pos 0070: 19 */ 0xF3 /* 's' -> */,
/* pos 0071: 20 */ 0xF4 /* 't' -> */,
/* pos 0072: 21 */ 0xBA /* ':' -> */,
/* pos 0073: 22 */ 0x00, 0x03 /* - terminal marker 3 - */,
/* pos 0075: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x007C state 24) */,
0x61 /* 'a' */, 0x72, 0x01 /* (to 0x01EA state 217) */,
0x08, /* fail */
/* pos 007c: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0083 state 25) */,
0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0206 state 243) */,
0x08, /* fail */
/* pos 0083: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x008A state 26) */,
0x74 /* 't' */, 0x86, 0x01 /* (to 0x020C state 248) */,
0x08, /* fail */
/* pos 008a: 26 */ 0xE5 /* 'e' -> */,
/* pos 008b: 27 */ 0xE3 /* 'c' -> */,
/* pos 008c: 28 */ 0xF4 /* 't' -> */,
/* pos 008d: 29 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0094 state 30) */,
0x20 /* ' ' */, 0xD2, 0x03 /* (to 0x0462 state 675) */,
0x08, /* fail */
/* pos 0094: 30 */ 0xEF /* 'o' -> */,
/* pos 0095: 31 */ 0xEE /* 'n' -> */,
/* pos 0096: 32 */ 0xBA /* ':' -> */,
/* pos 0097: 33 */ 0x00, 0x04 /* - terminal marker 4 - */,
/* pos 0099: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x00A3 state 35) */,
0x73 /* 's' */, 0x5F, 0x03 /* (to 0x03FB state 596) */,
0x72 /* 'r' */, 0x97, 0x03 /* (to 0x0436 state 642) */,
0x08, /* fail */
/* pos 00a3: 35 */ 0xE7 /* 'g' -> */,
/* pos 00a4: 36 */ 0xF2 /* 'r' -> */,
/* pos 00a5: 37 */ 0xE1 /* 'a' -> */,
/* pos 00a6: 38 */ 0xE4 /* 'd' -> */,
/* pos 00a7: 39 */ 0xE5 /* 'e' -> */,
/* pos 00a8: 40 */ 0xBA /* ':' -> */,
/* pos 00a9: 41 */ 0x00, 0x05 /* - terminal marker 5 - */,
/* pos 00ab: 42 */ 0xE9 /* 'i' -> */,
/* pos 00ac: 43 */ 0xE7 /* 'g' -> */,
/* pos 00ad: 44 */ 0xE9 /* 'i' -> */,
/* pos 00ae: 45 */ 0xEE /* 'n' -> */,
/* pos 00af: 46 */ 0xBA /* ':' -> */,
/* pos 00b0: 47 */ 0x00, 0x06 /* - terminal marker 6 - */,
/* pos 00b2: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B9 state 49) */,
0x74 /* 't' */, 0x13, 0x03 /* (to 0x03C8 state 553) */,
0x08, /* fail */
/* pos 00b9: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00C3 state 50) */,
0x72 /* 'r' */, 0xFC, 0x02 /* (to 0x03B8 state 539) */,
0x74 /* 't' */, 0xFF, 0x02 /* (to 0x03BE state 544) */,
0x08, /* fail */
/* pos 00c3: 50 */ 0xAD /* '-' -> */,
/* pos 00c4: 51 */ 0xF7 /* 'w' -> */,
/* pos 00c5: 52 */ 0xE5 /* 'e' -> */,
/* pos 00c6: 53 */ 0xE2 /* 'b' -> */,
/* pos 00c7: 54 */ 0xF3 /* 's' -> */,
/* pos 00c8: 55 */ 0xEF /* 'o' -> */,
/* pos 00c9: 56 */ 0xE3 /* 'c' -> */,
/* pos 00ca: 57 */ 0xEB /* 'k' -> */,
/* pos 00cb: 58 */ 0xE5 /* 'e' -> */,
/* pos 00cc: 59 */ 0xF4 /* 't' -> */,
/* pos 00cd: 60 */ 0xAD /* '-' -> */,
/* pos 00ce: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00E7 state 62) */,
0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00F1 state 70) */,
0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00FD state 81) */,
0x70 /* 'p' */, 0x38, 0x00 /* (to 0x010F state 88) */,
0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0119 state 97) */,
0x6E /* 'n' */, 0x44, 0x00 /* (to 0x0121 state 104) */,
0x76 /* 'v' */, 0x86, 0x01 /* (to 0x0266 state 284) */,
0x6F /* 'o' */, 0x8C, 0x01 /* (to 0x026F state 292) */,
0x08, /* fail */
/* pos 00e7: 62 */ 0xF2 /* 'r' -> */,
/* pos 00e8: 63 */ 0xE1 /* 'a' -> */,
/* pos 00e9: 64 */ 0xE6 /* 'f' -> */,
/* pos 00ea: 65 */ 0xF4 /* 't' -> */,
/* pos 00eb: 66 */ 0xBA /* ':' -> */,
/* pos 00ec: 67 */ 0x00, 0x07 /* - terminal marker 7 - */,
/* pos 00ee: 68 */ 0x8A /* '.' -> */,
/* pos 00ef: 69 */ 0x00, 0x08 /* - terminal marker 8 - */,
/* pos 00f1: 70 */ 0xF8 /* 'x' -> */,
/* pos 00f2: 71 */ 0xF4 /* 't' -> */,
/* pos 00f3: 72 */ 0xE5 /* 'e' -> */,
/* pos 00f4: 73 */ 0xEE /* 'n' -> */,
/* pos 00f5: 74 */ 0xF3 /* 's' -> */,
/* pos 00f6: 75 */ 0xE9 /* 'i' -> */,
/* pos 00f7: 76 */ 0xEF /* 'o' -> */,
/* pos 00f8: 77 */ 0xEE /* 'n' -> */,
/* pos 00f9: 78 */ 0xF3 /* 's' -> */,
/* pos 00fa: 79 */ 0xBA /* ':' -> */,
/* pos 00fb: 80 */ 0x00, 0x09 /* - terminal marker 9 - */,
/* pos 00fd: 81 */ 0xE5 /* 'e' -> */,
/* pos 00fe: 82 */ 0xF9 /* 'y' -> */,
/* pos 00ff: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0109 state 84) */,
0x32 /* '2' */, 0x0A, 0x00 /* (to 0x010C state 86) */,
0x3A /* ':' */, 0x5F, 0x01 /* (to 0x0264 state 283) */,
0x08, /* fail */
/* pos 0109: 84 */ 0xBA /* ':' -> */,
/* pos 010a: 85 */ 0x00, 0x0A /* - terminal marker 10 - */,
/* pos 010c: 86 */ 0xBA /* ':' -> */,
/* pos 010d: 87 */ 0x00, 0x0B /* - terminal marker 11 - */,
/* pos 010f: 88 */ 0xF2 /* 'r' -> */,
/* pos 0110: 89 */ 0xEF /* 'o' -> */,
/* pos 0111: 90 */ 0xF4 /* 't' -> */,
/* pos 0112: 91 */ 0xEF /* 'o' -> */,
/* pos 0113: 92 */ 0xE3 /* 'c' -> */,
/* pos 0114: 93 */ 0xEF /* 'o' -> */,
/* pos 0115: 94 */ 0xEC /* 'l' -> */,
/* pos 0116: 95 */ 0xBA /* ':' -> */,
/* pos 0117: 96 */ 0x00, 0x0C /* - terminal marker 12 - */,
/* pos 0119: 97 */ 0xE3 /* 'c' -> */,
/* pos 011a: 98 */ 0xE3 /* 'c' -> */,
/* pos 011b: 99 */ 0xE5 /* 'e' -> */,
/* pos 011c: 100 */ 0xF0 /* 'p' -> */,
/* pos 011d: 101 */ 0xF4 /* 't' -> */,
/* pos 011e: 102 */ 0xBA /* ':' -> */,
/* pos 011f: 103 */ 0x00, 0x0D /* - terminal marker 13 - */,
/* pos 0121: 104 */ 0xEF /* 'o' -> */,
/* pos 0122: 105 */ 0xEE /* 'n' -> */,
/* pos 0123: 106 */ 0xE3 /* 'c' -> */,
/* pos 0124: 107 */ 0xE5 /* 'e' -> */,
/* pos 0125: 108 */ 0xBA /* ':' -> */,
/* pos 0126: 109 */ 0x00, 0x0E /* - terminal marker 14 - */,
/* pos 0128: 110 */ 0xF4 /* 't' -> */,
/* pos 0129: 111 */ 0xF0 /* 'p' -> */,
/* pos 012a: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x0131 state 113) */,
0x32 /* '2' */, 0x10, 0x00 /* (to 0x013D state 118) */,
0x08, /* fail */
/* pos 0131: 113 */ 0xB1 /* '1' -> */,
/* pos 0132: 114 */ 0xAE /* '.' -> */,
/* pos 0133: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x013A state 116) */,
0x30 /* '0' */, 0x1B, 0x03 /* (to 0x0451 state 660) */,
0x08, /* fail */
/* pos 013a: 116 */ 0xA0 /* ' ' -> */,
/* pos 013b: 117 */ 0x00, 0x0F /* - terminal marker 15 - */,
/* pos 013d: 118 */ 0xAD /* '-' -> */,
/* pos 013e: 119 */ 0xF3 /* 's' -> */,
/* pos 013f: 120 */ 0xE5 /* 'e' -> */,
/* pos 0140: 121 */ 0xF4 /* 't' -> */,
/* pos 0141: 122 */ 0xF4 /* 't' -> */,
/* pos 0142: 123 */ 0xE9 /* 'i' -> */,
/* pos 0143: 124 */ 0xEE /* 'n' -> */,
/* pos 0144: 125 */ 0xE7 /* 'g' -> */,
/* pos 0145: 126 */ 0xF3 /* 's' -> */,
/* pos 0146: 127 */ 0xBA /* ':' -> */,
/* pos 0147: 128 */ 0x00, 0x10 /* - terminal marker 16 - */,
/* pos 0149: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0156 state 130) */,
0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01F8 state 230) */,
0x67 /* 'g' */, 0x7D, 0x01 /* (to 0x02CC state 358) */,
0x6C /* 'l' */, 0x7E, 0x01 /* (to 0x02D0 state 361) */,
0x08, /* fail */
/* pos 0156: 130 */ 0xE3 /* 'c' -> */,
/* pos 0157: 131 */ 0xE5 /* 'e' -> */,
/* pos 0158: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x015F state 133) */,
0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0169 state 136) */,
0x08, /* fail */
/* pos 015f: 133 */ 0xF4 /* 't' -> */,
/* pos 0160: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0167 state 135) */,
0x2D /* '-' */, 0x59, 0x00 /* (to 0x01BC state 192) */,
0x08, /* fail */
/* pos 0167: 135 */ 0x00, 0x11 /* - terminal marker 17 - */,
/* pos 0169: 136 */ 0xF3 /* 's' -> */,
/* pos 016a: 137 */ 0xAD /* '-' -> */,
/* pos 016b: 138 */ 0xE3 /* 'c' -> */,
/* pos 016c: 139 */ 0xEF /* 'o' -> */,
/* pos 016d: 140 */ 0xEE /* 'n' -> */,
/* pos 016e: 141 */ 0xF4 /* 't' -> */,
/* pos 016f: 142 */ 0xF2 /* 'r' -> */,
/* pos 0170: 143 */ 0xEF /* 'o' -> */,
/* pos 0171: 144 */ 0xEC /* 'l' -> */,
/* pos 0172: 145 */ 0xAD /* '-' -> */,
/* pos 0173: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x017A state 147) */,
0x61 /* 'a' */, 0x48, 0x01 /* (to 0x02BE state 345) */,
0x08, /* fail */
/* pos 017a: 147 */ 0xE5 /* 'e' -> */,
/* pos 017b: 148 */ 0xF1 /* 'q' -> */,
/* pos 017c: 149 */ 0xF5 /* 'u' -> */,
/* pos 017d: 150 */ 0xE5 /* 'e' -> */,
/* pos 017e: 151 */ 0xF3 /* 's' -> */,
/* pos 017f: 152 */ 0xF4 /* 't' -> */,
/* pos 0180: 153 */ 0xAD /* '-' -> */,
/* pos 0181: 154 */ 0xE8 /* 'h' -> */,
/* pos 0182: 155 */ 0xE5 /* 'e' -> */,
/* pos 0183: 156 */ 0xE1 /* 'a' -> */,
/* pos 0184: 157 */ 0xE4 /* 'd' -> */,
/* pos 0185: 158 */ 0xE5 /* 'e' -> */,
/* pos 0186: 159 */ 0xF2 /* 'r' -> */,
/* pos 0187: 160 */ 0xF3 /* 's' -> */,
/* pos 0188: 161 */ 0xBA /* ':' -> */,
/* pos 0189: 162 */ 0x00, 0x12 /* - terminal marker 18 - */,
/* pos 018b: 163 */ 0xE6 /* 'f' -> */,
/* pos 018c: 164 */ 0xAD /* '-' -> */,
/* pos 018d: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x019A state 166) */,
0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01B0 state 181) */,
0x72 /* 'r' */, 0x9E, 0x01 /* (to 0x0331 state 435) */,
0x75 /* 'u' */, 0xA2, 0x01 /* (to 0x0338 state 441) */,
0x08, /* fail */
/* pos 019a: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x01A1 state 167) */,
0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x032B state 430) */,
0x08, /* fail */
/* pos 01a1: 167 */ 0xE4 /* 'd' -> */,
/* pos 01a2: 168 */ 0xE9 /* 'i' -> */,
/* pos 01a3: 169 */ 0xE6 /* 'f' -> */,
/* pos 01a4: 170 */ 0xE9 /* 'i' -> */,
/* pos 01a5: 171 */ 0xE5 /* 'e' -> */,
/* pos 01a6: 172 */ 0xE4 /* 'd' -> */,
/* pos 01a7: 173 */ 0xAD /* '-' -> */,
/* pos 01a8: 174 */ 0xF3 /* 's' -> */,
/* pos 01a9: 175 */ 0xE9 /* 'i' -> */,
/* pos 01aa: 176 */ 0xEE /* 'n' -> */,
/* pos 01ab: 177 */ 0xE3 /* 'c' -> */,
/* pos 01ac: 178 */ 0xE5 /* 'e' -> */,
/* pos 01ad: 179 */ 0xBA /* ':' -> */,
/* pos 01ae: 180 */ 0x00, 0x13 /* - terminal marker 19 - */,
/* pos 01b0: 181 */ 0xEF /* 'o' -> */,
/* pos 01b1: 182 */ 0xEE /* 'n' -> */,
/* pos 01b2: 183 */ 0xE5 /* 'e' -> */,
/* pos 01b3: 184 */ 0xAD /* '-' -> */,
/* pos 01b4: 185 */ 0xED /* 'm' -> */,
/* pos 01b5: 186 */ 0xE1 /* 'a' -> */,
/* pos 01b6: 187 */ 0xF4 /* 't' -> */,
/* pos 01b7: 188 */ 0xE3 /* 'c' -> */,
/* pos 01b8: 189 */ 0xE8 /* 'h' -> */,
/* pos 01b9: 190 */ 0xBA /* ':' -> */,
/* pos 01ba: 191 */ 0x00, 0x14 /* - terminal marker 20 - */,
/* pos 01bc: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C9 state 193) */,
0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01D3 state 202) */,
0x63 /* 'c' */, 0xEB, 0x00 /* (to 0x02AD state 330) */,
0x72 /* 'r' */, 0xF1, 0x00 /* (to 0x02B6 state 338) */,
0x08, /* fail */
/* pos 01c9: 193 */ 0xEE /* 'n' -> */,
/* pos 01ca: 194 */ 0xE3 /* 'c' -> */,
/* pos 01cb: 195 */ 0xEF /* 'o' -> */,
/* pos 01cc: 196 */ 0xE4 /* 'd' -> */,
/* pos 01cd: 197 */ 0xE9 /* 'i' -> */,
/* pos 01ce: 198 */ 0xEE /* 'n' -> */,
/* pos 01cf: 199 */ 0xE7 /* 'g' -> */,
/* pos 01d0: 200 */ 0xBA /* ':' -> */,
/* pos 01d1: 201 */ 0x00, 0x15 /* - terminal marker 21 - */,
/* pos 01d3: 202 */ 0xE1 /* 'a' -> */,
/* pos 01d4: 203 */ 0xEE /* 'n' -> */,
/* pos 01d5: 204 */ 0xE7 /* 'g' -> */,
/* pos 01d6: 205 */ 0xF5 /* 'u' -> */,
/* pos 01d7: 206 */ 0xE1 /* 'a' -> */,
/* pos 01d8: 207 */ 0xE7 /* 'g' -> */,
/* pos 01d9: 208 */ 0xE5 /* 'e' -> */,
/* pos 01da: 209 */ 0xBA /* ':' -> */,
/* pos 01db: 210 */ 0x00, 0x16 /* - terminal marker 22 - */,
/* pos 01dd: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01E4 state 212) */,
0x6F /* 'o' */, 0x9E, 0x01 /* (to 0x037E state 497) */,
0x08, /* fail */
/* pos 01e4: 212 */ 0xE7 /* 'g' -> */,
/* pos 01e5: 213 */ 0xED /* 'm' -> */,
/* pos 01e6: 214 */ 0xE1 /* 'a' -> */,
/* pos 01e7: 215 */ 0xBA /* ':' -> */,
/* pos 01e8: 216 */ 0x00, 0x17 /* - terminal marker 23 - */,
/* pos 01ea: 217 */ 0xE3 /* 'c' -> */,
/* pos 01eb: 218 */ 0xE8 /* 'h' -> */,
/* pos 01ec: 219 */ 0xE5 /* 'e' -> */,
/* pos 01ed: 220 */ 0xAD /* '-' -> */,
/* pos 01ee: 221 */ 0xE3 /* 'c' -> */,
/* pos 01ef: 222 */ 0xEF /* 'o' -> */,
/* pos 01f0: 223 */ 0xEE /* 'n' -> */,
/* pos 01f1: 224 */ 0xF4 /* 't' -> */,
/* pos 01f2: 225 */ 0xF2 /* 'r' -> */,
/* pos 01f3: 226 */ 0xEF /* 'o' -> */,
/* pos 01f4: 227 */ 0xEC /* 'l' -> */,
/* pos 01f5: 228 */ 0xBA /* ':' -> */,
/* pos 01f6: 229 */ 0x00, 0x18 /* - terminal marker 24 - */,
/* pos 01f8: 230 */ 0xF4 /* 't' -> */,
/* pos 01f9: 231 */ 0xE8 /* 'h' -> */,
/* pos 01fa: 232 */ 0xEF /* 'o' -> */,
/* pos 01fb: 233 */ 0xF2 /* 'r' -> */,
/* pos 01fc: 234 */ 0xE9 /* 'i' -> */,
/* pos 01fd: 235 */ 0xFA /* 'z' -> */,
/* pos 01fe: 236 */ 0xE1 /* 'a' -> */,
/* pos 01ff: 237 */ 0xF4 /* 't' -> */,
/* pos 0200: 238 */ 0xE9 /* 'i' -> */,
/* pos 0201: 239 */ 0xEF /* 'o' -> */,
/* pos 0202: 240 */ 0xEE /* 'n' -> */,
/* pos 0203: 241 */ 0xBA /* ':' -> */,
/* pos 0204: 242 */ 0x00, 0x19 /* - terminal marker 25 - */,
/* pos 0206: 243 */ 0xEB /* 'k' -> */,
/* pos 0207: 244 */ 0xE9 /* 'i' -> */,
/* pos 0208: 245 */ 0xE5 /* 'e' -> */,
/* pos 0209: 246 */ 0xBA /* ':' -> */,
/* pos 020a: 247 */ 0x00, 0x1A /* - terminal marker 26 - */,
/* pos 020c: 248 */ 0xE5 /* 'e' -> */,
/* pos 020d: 249 */ 0xEE /* 'n' -> */,
/* pos 020e: 250 */ 0xF4 /* 't' -> */,
/* pos 020f: 251 */ 0xAD /* '-' -> */,
/* pos 0210: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0220 state 253) */,
0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0231 state 260) */,
0x64 /* 'd' */, 0xC0, 0x00 /* (to 0x02D6 state 366) */,
0x65 /* 'e' */, 0xCA, 0x00 /* (to 0x02E3 state 378) */,
0x72 /* 'r' */, 0xE3, 0x00 /* (to 0x02FF state 403) */,
0x08, /* fail */
/* pos 0220: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x022A state 254) */,
0x61 /* 'a' */, 0xCA, 0x00 /* (to 0x02ED state 387) */,
0x6F /* 'o' */, 0xD0, 0x00 /* (to 0x02F6 state 395) */,
0x08, /* fail */
/* pos 022a: 254 */ 0xEE /* 'n' -> */,
/* pos 022b: 255 */ 0xE7 /* 'g' -> */,
/* pos 022c: 256 */ 0xF4 /* 't' -> */,
/* pos 022d: 257 */ 0xE8 /* 'h' -> */,
/* pos 022e: 258 */ 0xBA /* ':' -> */,
/* pos 022f: 259 */ 0x00, 0x1B /* - terminal marker 27 - */,
/* pos 0231: 260 */ 0xF9 /* 'y' -> */,
/* pos 0232: 261 */ 0xF0 /* 'p' -> */,
/* pos 0233: 262 */ 0xE5 /* 'e' -> */,
/* pos 0234: 263 */ 0xBA /* ':' -> */,
/* pos 0235: 264 */ 0x00, 0x1C /* - terminal marker 28 - */,
/* pos 0237: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x023E state 266) */,
0x65 /* 'e' */, 0xF6, 0x01 /* (to 0x0430 state 637) */,
0x08, /* fail */
/* pos 023e: 266 */ 0xF4 /* 't' -> */,
/* pos 023f: 267 */ 0xE5 /* 'e' -> */,
/* pos 0240: 268 */ 0xBA /* ':' -> */,
/* pos 0241: 269 */ 0x00, 0x1D /* - terminal marker 29 - */,
/* pos 0243: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x024A state 271) */,
0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0250 state 276) */,
0x08, /* fail */
/* pos 024a: 271 */ 0xEE /* 'n' -> */,
/* pos 024b: 272 */ 0xE7 /* 'g' -> */,
/* pos 024c: 273 */ 0xE5 /* 'e' -> */,
/* pos 024d: 274 */ 0xBA /* ':' -> */,
/* pos 024e: 275 */ 0x00, 0x1E /* - terminal marker 30 - */,
/* pos 0250: 276 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0257 state 277) */,
0x74 /* 't' */, 0x5A, 0x01 /* (to 0x03AD state 529) */,
0x08, /* fail */
/* pos 0257: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x025E state 278) */,
0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x03A7 state 524) */,
0x08, /* fail */
/* pos 025e: 278 */ 0xF2 /* 'r' -> */,
/* pos 025f: 279 */ 0xE5 /* 'e' -> */,
/* pos 0260: 280 */ 0xF2 /* 'r' -> */,
/* pos 0261: 281 */ 0xBA /* ':' -> */,
/* pos 0262: 282 */ 0x00, 0x1F /* - terminal marker 31 - */,
/* pos 0264: 283 */ 0x00, 0x20 /* - terminal marker 32 - */,
/* pos 0266: 284 */ 0xE5 /* 'e' -> */,
/* pos 0267: 285 */ 0xF2 /* 'r' -> */,
/* pos 0268: 286 */ 0xF3 /* 's' -> */,
/* pos 0269: 287 */ 0xE9 /* 'i' -> */,
/* pos 026a: 288 */ 0xEF /* 'o' -> */,
/* pos 026b: 289 */ 0xEE /* 'n' -> */,
/* pos 026c: 290 */ 0xBA /* ':' -> */,
/* pos 026d: 291 */ 0x00, 0x21 /* - terminal marker 33 - */,
/* pos 026f: 292 */ 0xF2 /* 'r' -> */,
/* pos 0270: 293 */ 0xE9 /* 'i' -> */,
/* pos 0271: 294 */ 0xE7 /* 'g' -> */,
/* pos 0272: 295 */ 0xE9 /* 'i' -> */,
/* pos 0273: 296 */ 0xEE /* 'n' -> */,
/* pos 0274: 297 */ 0xBA /* ':' -> */,
/* pos 0275: 298 */ 0x00, 0x22 /* - terminal marker 34 - */,
/* pos 0277: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0284 state 300) */,
0x6D /* 'm' */, 0x14, 0x00 /* (to 0x028E state 309) */,
0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0295 state 315) */,
0x73 /* 's' */, 0x1A, 0x00 /* (to 0x029A state 319) */,
0x08, /* fail */
/* pos 0284: 300 */ 0xF5 /* 'u' -> */,
/* pos 0285: 301 */ 0xF4 /* 't' -> */,
/* pos 0286: 302 */ 0xE8 /* 'h' -> */,
/* pos 0287: 303 */ 0xEF /* 'o' -> */,
/* pos 0288: 304 */ 0xF2 /* 'r' -> */,
/* pos 0289: 305 */ 0xE9 /* 'i' -> */,
/* pos 028a: 306 */ 0xF4 /* 't' -> */,
/* pos 028b: 307 */ 0xF9 /* 'y' -> */,
/* pos 028c: 308 */ 0x00, 0x23 /* - terminal marker 35 - */,
/* pos 028e: 309 */ 0xE5 /* 'e' -> */,
/* pos 028f: 310 */ 0xF4 /* 't' -> */,
/* pos 0290: 311 */ 0xE8 /* 'h' -> */,
/* pos 0291: 312 */ 0xEF /* 'o' -> */,
/* pos 0292: 313 */ 0xE4 /* 'd' -> */,
/* pos 0293: 314 */ 0x00, 0x24 /* - terminal marker 36 - */,
/* pos 0295: 315 */ 0xE1 /* 'a' -> */,
/* pos 0296: 316 */ 0xF4 /* 't' -> */,
/* pos 0297: 317 */ 0xE8 /* 'h' -> */,
/* pos 0298: 318 */ 0x00, 0x25 /* - terminal marker 37 - */,
/* pos 029a: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x02A1 state 320) */,
0x74 /* 't' */, 0x0A, 0x00 /* (to 0x02A7 state 325) */,
0x08, /* fail */
/* pos 02a1: 320 */ 0xE8 /* 'h' -> */,
/* pos 02a2: 321 */ 0xE5 /* 'e' -> */,
/* pos 02a3: 322 */ 0xED /* 'm' -> */,
/* pos 02a4: 323 */ 0xE5 /* 'e' -> */,
/* pos 02a5: 324 */ 0x00, 0x26 /* - terminal marker 38 - */,
/* pos 02a7: 325 */ 0xE1 /* 'a' -> */,
/* pos 02a8: 326 */ 0xF4 /* 't' -> */,
/* pos 02a9: 327 */ 0xF5 /* 'u' -> */,
/* pos 02aa: 328 */ 0xF3 /* 's' -> */,
/* pos 02ab: 329 */ 0x00, 0x27 /* - terminal marker 39 - */,
/* pos 02ad: 330 */ 0xE8 /* 'h' -> */,
/* pos 02ae: 331 */ 0xE1 /* 'a' -> */,
/* pos 02af: 332 */ 0xF2 /* 'r' -> */,
/* pos 02b0: 333 */ 0xF3 /* 's' -> */,
/* pos 02b1: 334 */ 0xE5 /* 'e' -> */,
/* pos 02b2: 335 */ 0xF4 /* 't' -> */,
/* pos 02b3: 336 */ 0xBA /* ':' -> */,
/* pos 02b4: 337 */ 0x00, 0x28 /* - terminal marker 40 - */,
/* pos 02b6: 338 */ 0xE1 /* 'a' -> */,
/* pos 02b7: 339 */ 0xEE /* 'n' -> */,
/* pos 02b8: 340 */ 0xE7 /* 'g' -> */,
/* pos 02b9: 341 */ 0xE5 /* 'e' -> */,
/* pos 02ba: 342 */ 0xF3 /* 's' -> */,
/* pos 02bb: 343 */ 0xBA /* ':' -> */,
/* pos 02bc: 344 */ 0x00, 0x29 /* - terminal marker 41 - */,
/* pos 02be: 345 */ 0xEC /* 'l' -> */,
/* pos 02bf: 346 */ 0xEC /* 'l' -> */,
/* pos 02c0: 347 */ 0xEF /* 'o' -> */,
/* pos 02c1: 348 */ 0xF7 /* 'w' -> */,
/* pos 02c2: 349 */ 0xAD /* '-' -> */,
/* pos 02c3: 350 */ 0xEF /* 'o' -> */,
/* pos 02c4: 351 */ 0xF2 /* 'r' -> */,
/* pos 02c5: 352 */ 0xE9 /* 'i' -> */,
/* pos 02c6: 353 */ 0xE7 /* 'g' -> */,
/* pos 02c7: 354 */ 0xE9 /* 'i' -> */,
/* pos 02c8: 355 */ 0xEE /* 'n' -> */,
/* pos 02c9: 356 */ 0xBA /* ':' -> */,
/* pos 02ca: 357 */ 0x00, 0x2A /* - terminal marker 42 - */,
/* pos 02cc: 358 */ 0xE5 /* 'e' -> */,
/* pos 02cd: 359 */ 0xBA /* ':' -> */,
/* pos 02ce: 360 */ 0x00, 0x2B /* - terminal marker 43 - */,
/* pos 02d0: 361 */ 0xEC /* 'l' -> */,
/* pos 02d1: 362 */ 0xEF /* 'o' -> */,
/* pos 02d2: 363 */ 0xF7 /* 'w' -> */,
/* pos 02d3: 364 */ 0xBA /* ':' -> */,
/* pos 02d4: 365 */ 0x00, 0x2C /* - terminal marker 44 - */,
/* pos 02d6: 366 */ 0xE9 /* 'i' -> */,
/* pos 02d7: 367 */ 0xF3 /* 's' -> */,
/* pos 02d8: 368 */ 0xF0 /* 'p' -> */,
/* pos 02d9: 369 */ 0xEF /* 'o' -> */,
/* pos 02da: 370 */ 0xF3 /* 's' -> */,
/* pos 02db: 371 */ 0xE9 /* 'i' -> */,
/* pos 02dc: 372 */ 0xF4 /* 't' -> */,
/* pos 02dd: 373 */ 0xE9 /* 'i' -> */,
/* pos 02de: 374 */ 0xEF /* 'o' -> */,
/* pos 02df: 375 */ 0xEE /* 'n' -> */,
/* pos 02e0: 376 */ 0xBA /* ':' -> */,
/* pos 02e1: 377 */ 0x00, 0x2D /* - terminal marker 45 - */,
/* pos 02e3: 378 */ 0xEE /* 'n' -> */,
/* pos 02e4: 379 */ 0xE3 /* 'c' -> */,
/* pos 02e5: 380 */ 0xEF /* 'o' -> */,
/* pos 02e6: 381 */ 0xE4 /* 'd' -> */,
/* pos 02e7: 382 */ 0xE9 /* 'i' -> */,
/* pos 02e8: 383 */ 0xEE /* 'n' -> */,
/* pos 02e9: 384 */ 0xE7 /* 'g' -> */,
/* pos 02ea: 385 */ 0xBA /* ':' -> */,
/* pos 02eb: 386 */ 0x00, 0x2E /* - terminal marker 46 - */,
/* pos 02ed: 387 */ 0xEE /* 'n' -> */,
/* pos 02ee: 388 */ 0xE7 /* 'g' -> */,
/* pos 02ef: 389 */ 0xF5 /* 'u' -> */,
/* pos 02f0: 390 */ 0xE1 /* 'a' -> */,
/* pos 02f1: 391 */ 0xE7 /* 'g' -> */,
/* pos 02f2: 392 */ 0xE5 /* 'e' -> */,
/* pos 02f3: 393 */ 0xBA /* ':' -> */,
/* pos 02f4: 394 */ 0x00, 0x2F /* - terminal marker 47 - */,
/* pos 02f6: 395 */ 0xE3 /* 'c' -> */,
/* pos 02f7: 396 */ 0xE1 /* 'a' -> */,
/* pos 02f8: 397 */ 0xF4 /* 't' -> */,
/* pos 02f9: 398 */ 0xE9 /* 'i' -> */,
/* pos 02fa: 399 */ 0xEF /* 'o' -> */,
/* pos 02fb: 400 */ 0xEE /* 'n' -> */,
/* pos 02fc: 401 */ 0xBA /* ':' -> */,
/* pos 02fd: 402 */ 0x00, 0x30 /* - terminal marker 48 - */,
/* pos 02ff: 403 */ 0xE1 /* 'a' -> */,
/* pos 0300: 404 */ 0xEE /* 'n' -> */,
/* pos 0301: 405 */ 0xE7 /* 'g' -> */,
/* pos 0302: 406 */ 0xE5 /* 'e' -> */,
/* pos 0303: 407 */ 0xBA /* ':' -> */,
/* pos 0304: 408 */ 0x00, 0x31 /* - terminal marker 49 - */,
/* pos 0306: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x030D state 410) */,
0x78 /* 'x' */, 0x09, 0x00 /* (to 0x0312 state 414) */,
0x08, /* fail */
/* pos 030d: 410 */ 0xE1 /* 'a' -> */,
/* pos 030e: 411 */ 0xE7 /* 'g' -> */,
/* pos 030f: 412 */ 0xBA /* ':' -> */,
/* pos 0310: 413 */ 0x00, 0x32 /* - terminal marker 50 - */,
/* pos 0312: 414 */ 0xF0 /* 'p' -> */,
/* pos 0313: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x031A state 416) */,
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x031F state 420) */,
0x08, /* fail */
/* pos 031a: 416 */ 0xE3 /* 'c' -> */,
/* pos 031b: 417 */ 0xF4 /* 't' -> */,
/* pos 031c: 418 */ 0xBA /* ':' -> */,
/* pos 031d: 419 */ 0x00, 0x33 /* - terminal marker 51 - */,
/* pos 031f: 420 */ 0xF2 /* 'r' -> */,
/* pos 0320: 421 */ 0xE5 /* 'e' -> */,
/* pos 0321: 422 */ 0xF3 /* 's' -> */,
/* pos 0322: 423 */ 0xBA /* ':' -> */,
/* pos 0323: 424 */ 0x00, 0x34 /* - terminal marker 52 - */,
/* pos 0325: 425 */ 0xF2 /* 'r' -> */,
/* pos 0326: 426 */ 0xEF /* 'o' -> */,
/* pos 0327: 427 */ 0xED /* 'm' -> */,
/* pos 0328: 428 */ 0xBA /* ':' -> */,
/* pos 0329: 429 */ 0x00, 0x35 /* - terminal marker 53 - */,
/* pos 032b: 430 */ 0xF4 /* 't' -> */,
/* pos 032c: 431 */ 0xE3 /* 'c' -> */,
/* pos 032d: 432 */ 0xE8 /* 'h' -> */,
/* pos 032e: 433 */ 0xBA /* ':' -> */,
/* pos 032f: 434 */ 0x00, 0x36 /* - terminal marker 54 - */,
/* pos 0331: 435 */ 0xE1 /* 'a' -> */,
/* pos 0332: 436 */ 0xEE /* 'n' -> */,
/* pos 0333: 437 */ 0xE7 /* 'g' -> */,
/* pos 0334: 438 */ 0xE5 /* 'e' -> */,
/* pos 0335: 439 */ 0xBA /* ':' -> */,
/* pos 0336: 440 */ 0x00, 0x37 /* - terminal marker 55 - */,
/* pos 0338: 441 */ 0xEE /* 'n' -> */,
/* pos 0339: 442 */ 0xED /* 'm' -> */,
/* pos 033a: 443 */ 0xEF /* 'o' -> */,
/* pos 033b: 444 */ 0xE4 /* 'd' -> */,
/* pos 033c: 445 */ 0xE9 /* 'i' -> */,
/* pos 033d: 446 */ 0xE6 /* 'f' -> */,
/* pos 033e: 447 */ 0xE9 /* 'i' -> */,
/* pos 033f: 448 */ 0xE5 /* 'e' -> */,
/* pos 0340: 449 */ 0xE4 /* 'd' -> */,
/* pos 0341: 450 */ 0xAD /* '-' -> */,
/* pos 0342: 451 */ 0xF3 /* 's' -> */,
/* pos 0343: 452 */ 0xE9 /* 'i' -> */,
/* pos 0344: 453 */ 0xEE /* 'n' -> */,
/* pos 0345: 454 */ 0xE3 /* 'c' -> */,
/* pos 0346: 455 */ 0xE5 /* 'e' -> */,
/* pos 0347: 456 */ 0xBA /* ':' -> */,
/* pos 0348: 457 */ 0x00, 0x38 /* - terminal marker 56 - */,
/* pos 034a: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0354 state 459) */,
0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0362 state 472) */,
0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0367 state 476) */,
0x08, /* fail */
/* pos 0354: 459 */ 0xF3 /* 's' -> */,
/* pos 0355: 460 */ 0xF4 /* 't' -> */,
/* pos 0356: 461 */ 0xAD /* '-' -> */,
/* pos 0357: 462 */ 0xED /* 'm' -> */,
/* pos 0358: 463 */ 0xEF /* 'o' -> */,
/* pos 0359: 464 */ 0xE4 /* 'd' -> */,
/* pos 035a: 465 */ 0xE9 /* 'i' -> */,
/* pos 035b: 466 */ 0xE6 /* 'f' -> */,
/* pos 035c: 467 */ 0xE9 /* 'i' -> */,
/* pos 035d: 468 */ 0xE5 /* 'e' -> */,
/* pos 035e: 469 */ 0xE4 /* 'd' -> */,
/* pos 035f: 470 */ 0xBA /* ':' -> */,
/* pos 0360: 471 */ 0x00, 0x39 /* - terminal marker 57 - */,
/* pos 0362: 472 */ 0xEE /* 'n' -> */,
/* pos 0363: 473 */ 0xEB /* 'k' -> */,
/* pos 0364: 474 */ 0xBA /* ':' -> */,
/* pos 0365: 475 */ 0x00, 0x3A /* - terminal marker 58 - */,
/* pos 0367: 476 */ 0xE3 /* 'c' -> */,
/* pos 0368: 477 */ 0xE1 /* 'a' -> */,
/* pos 0369: 478 */ 0xF4 /* 't' -> */,
/* pos 036a: 479 */ 0xE9 /* 'i' -> */,
/* pos 036b: 480 */ 0xEF /* 'o' -> */,
/* pos 036c: 481 */ 0xEE /* 'n' -> */,
/* pos 036d: 482 */ 0xBA /* ':' -> */,
/* pos 036e: 483 */ 0x00, 0x3B /* - terminal marker 59 - */,
/* pos 0370: 484 */ 0xE1 /* 'a' -> */,
/* pos 0371: 485 */ 0xF8 /* 'x' -> */,
/* pos 0372: 486 */ 0xAD /* '-' -> */,
/* pos 0373: 487 */ 0xE6 /* 'f' -> */,
/* pos 0374: 488 */ 0xEF /* 'o' -> */,
/* pos 0375: 489 */ 0xF2 /* 'r' -> */,
/* pos 0376: 490 */ 0xF7 /* 'w' -> */,
/* pos 0377: 491 */ 0xE1 /* 'a' -> */,
/* pos 0378: 492 */ 0xF2 /* 'r' -> */,
/* pos 0379: 493 */ 0xE4 /* 'd' -> */,
/* pos 037a: 494 */ 0xF3 /* 's' -> */,
/* pos 037b: 495 */ 0xBA /* ':' -> */,
/* pos 037c: 496 */ 0x00, 0x3C /* - terminal marker 60 - */,
/* pos 037e: 497 */ 0xF8 /* 'x' -> */,
/* pos 037f: 498 */ 0xF9 /* 'y' -> */,
/* pos 0380: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0387 state 500) */,
0x20 /* ' ' */, 0xBB, 0x00 /* (to 0x043E state 649) */,
0x08, /* fail */
/* pos 0387: 500 */ 0xE1 /* 'a' -> */,
/* pos 0388: 501 */ 0xF5 /* 'u' -> */,
/* pos 0389: 502 */ 0xF4 /* 't' -> */,
/* pos 038a: 503 */ 0xE8 /* 'h' -> */,
/* pos 038b: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0392 state 505) */,
0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x039C state 514) */,
0x08, /* fail */
/* pos 0392: 505 */ 0xEE /* 'n' -> */,
/* pos 0393: 506 */ 0xF4 /* 't' -> */,
/* pos 0394: 507 */ 0xE9 /* 'i' -> */,
/* pos 0395: 508 */ 0xE3 /* 'c' -> */,
/* pos 0396: 509 */ 0xE1 /* 'a' -> */,
/* pos 0397: 510 */ 0xF4 /* 't' -> */,
/* pos 0398: 511 */ 0xE5 /* 'e' -> */,
/* pos 0399: 512 */ 0xBA /* ':' -> */,
/* pos 039a: 513 */ 0x00, 0x3D /* - terminal marker 61 - */,
/* pos 039c: 514 */ 0xF2 /* 'r' -> */,
/* pos 039d: 515 */ 0xE9 /* 'i' -> */,
/* pos 039e: 516 */ 0xFA /* 'z' -> */,
/* pos 039f: 517 */ 0xE1 /* 'a' -> */,
/* pos 03a0: 518 */ 0xF4 /* 't' -> */,
/* pos 03a1: 519 */ 0xE9 /* 'i' -> */,
/* pos 03a2: 520 */ 0xEF /* 'o' -> */,
/* pos 03a3: 521 */ 0xEE /* 'n' -> */,
/* pos 03a4: 522 */ 0xBA /* ':' -> */,
/* pos 03a5: 523 */ 0x00, 0x3E /* - terminal marker 62 - */,
/* pos 03a7: 524 */ 0xE5 /* 'e' -> */,
/* pos 03a8: 525 */ 0xF3 /* 's' -> */,
/* pos 03a9: 526 */ 0xE8 /* 'h' -> */,
/* pos 03aa: 527 */ 0xBA /* ':' -> */,
/* pos 03ab: 528 */ 0x00, 0x3F /* - terminal marker 63 - */,
/* pos 03ad: 529 */ 0xF2 /* 'r' -> */,
/* pos 03ae: 530 */ 0xF9 /* 'y' -> */,
/* pos 03af: 531 */ 0xAD /* '-' -> */,
/* pos 03b0: 532 */ 0xE1 /* 'a' -> */,
/* pos 03b1: 533 */ 0xE6 /* 'f' -> */,
/* pos 03b2: 534 */ 0xF4 /* 't' -> */,
/* pos 03b3: 535 */ 0xE5 /* 'e' -> */,
/* pos 03b4: 536 */ 0xF2 /* 'r' -> */,
/* pos 03b5: 537 */ 0xBA /* ':' -> */,
/* pos 03b6: 538 */ 0x00, 0x40 /* - terminal marker 64 - */,
/* pos 03b8: 539 */ 0xF6 /* 'v' -> */,
/* pos 03b9: 540 */ 0xE5 /* 'e' -> */,
/* pos 03ba: 541 */ 0xF2 /* 'r' -> */,
/* pos 03bb: 542 */ 0xBA /* ':' -> */,
/* pos 03bc: 543 */ 0x00, 0x41 /* - terminal marker 65 - */,
/* pos 03be: 544 */ 0xAD /* '-' -> */,
/* pos 03bf: 545 */ 0xE3 /* 'c' -> */,
/* pos 03c0: 546 */ 0xEF /* 'o' -> */,
/* pos 03c1: 547 */ 0xEF /* 'o' -> */,
/* pos 03c2: 548 */ 0xEB /* 'k' -> */,
/* pos 03c3: 549 */ 0xE9 /* 'i' -> */,
/* pos 03c4: 550 */ 0xE5 /* 'e' -> */,
/* pos 03c5: 551 */ 0xBA /* ':' -> */,
/* pos 03c6: 552 */ 0x00, 0x42 /* - terminal marker 66 - */,
/* pos 03c8: 553 */ 0xF2 /* 'r' -> */,
/* pos 03c9: 554 */ 0xE9 /* 'i' -> */,
/* pos 03ca: 555 */ 0xE3 /* 'c' -> */,
/* pos 03cb: 556 */ 0xF4 /* 't' -> */,
/* pos 03cc: 557 */ 0xAD /* '-' -> */,
/* pos 03cd: 558 */ 0xF4 /* 't' -> */,
/* pos 03ce: 559 */ 0xF2 /* 'r' -> */,
/* pos 03cf: 560 */ 0xE1 /* 'a' -> */,
/* pos 03d0: 561 */ 0xEE /* 'n' -> */,
/* pos 03d1: 562 */ 0xF3 /* 's' -> */,
/* pos 03d2: 563 */ 0xF0 /* 'p' -> */,
/* pos 03d3: 564 */ 0xEF /* 'o' -> */,
/* pos 03d4: 565 */ 0xF2 /* 'r' -> */,
/* pos 03d5: 566 */ 0xF4 /* 't' -> */,
/* pos 03d6: 567 */ 0xAD /* '-' -> */,
/* pos 03d7: 568 */ 0xF3 /* 's' -> */,
/* pos 03d8: 569 */ 0xE5 /* 'e' -> */,
/* pos 03d9: 570 */ 0xE3 /* 'c' -> */,
/* pos 03da: 571 */ 0xF5 /* 'u' -> */,
/* pos 03db: 572 */ 0xF2 /* 'r' -> */,
/* pos 03dc: 573 */ 0xE9 /* 'i' -> */,
/* pos 03dd: 574 */ 0xF4 /* 't' -> */,
/* pos 03de: 575 */ 0xF9 /* 'y' -> */,
/* pos 03df: 576 */ 0xBA /* ':' -> */,
/* pos 03e0: 577 */ 0x00, 0x43 /* - terminal marker 67 - */,
/* pos 03e2: 578 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x03E9 state 579) */,
0x65 /* 'e' */, 0x84, 0x00 /* (to 0x0469 state 680) */,
0x08, /* fail */
/* pos 03e9: 579 */ 0xE1 /* 'a' -> */,
/* pos 03ea: 580 */ 0xEE /* 'n' -> */,
/* pos 03eb: 581 */ 0xF3 /* 's' -> */,
/* pos 03ec: 582 */ 0xE6 /* 'f' -> */,
/* pos 03ed: 583 */ 0xE5 /* 'e' -> */,
/* pos 03ee: 584 */ 0xF2 /* 'r' -> */,
/* pos 03ef: 585 */ 0xAD /* '-' -> */,
/* pos 03f0: 586 */ 0xE5 /* 'e' -> */,
/* pos 03f1: 587 */ 0xEE /* 'n' -> */,
/* pos 03f2: 588 */ 0xE3 /* 'c' -> */,
/* pos 03f3: 589 */ 0xEF /* 'o' -> */,
/* pos 03f4: 590 */ 0xE4 /* 'd' -> */,
/* pos 03f5: 591 */ 0xE9 /* 'i' -> */,
/* pos 03f6: 592 */ 0xEE /* 'n' -> */,
/* pos 03f7: 593 */ 0xE7 /* 'g' -> */,
/* pos 03f8: 594 */ 0xBA /* ':' -> */,
/* pos 03f9: 595 */ 0x00, 0x44 /* - terminal marker 68 - */,
/* pos 03fb: 596 */ 0xE5 /* 'e' -> */,
/* pos 03fc: 597 */ 0xF2 /* 'r' -> */,
/* pos 03fd: 598 */ 0xAD /* '-' -> */,
/* pos 03fe: 599 */ 0xE1 /* 'a' -> */,
/* pos 03ff: 600 */ 0xE7 /* 'g' -> */,
/* pos 0400: 601 */ 0xE5 /* 'e' -> */,
/* pos 0401: 602 */ 0xEE /* 'n' -> */,
/* pos 0402: 603 */ 0xF4 /* 't' -> */,
/* pos 0403: 604 */ 0xBA /* ':' -> */,
/* pos 0404: 605 */ 0x00, 0x45 /* - terminal marker 69 - */,
/* pos 0406: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x040D state 607) */,
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0412 state 611) */,
0x08, /* fail */
/* pos 040d: 607 */ 0xF2 /* 'r' -> */,
/* pos 040e: 608 */ 0xF9 /* 'y' -> */,
/* pos 040f: 609 */ 0xBA /* ':' -> */,
/* pos 0410: 610 */ 0x00, 0x46 /* - terminal marker 70 - */,
/* pos 0412: 611 */ 0xE1 /* 'a' -> */,
/* pos 0413: 612 */ 0xBA /* ':' -> */,
/* pos 0414: 613 */ 0x00, 0x47 /* - terminal marker 71 - */,
/* pos 0416: 614 */ 0xF7 /* 'w' -> */,
/* pos 0417: 615 */ 0xF7 /* 'w' -> */,
/* pos 0418: 616 */ 0xAD /* '-' -> */,
/* pos 0419: 617 */ 0xE1 /* 'a' -> */,
/* pos 041a: 618 */ 0xF5 /* 'u' -> */,
/* pos 041b: 619 */ 0xF4 /* 't' -> */,
/* pos 041c: 620 */ 0xE8 /* 'h' -> */,
/* pos 041d: 621 */ 0xE5 /* 'e' -> */,
/* pos 041e: 622 */ 0xEE /* 'n' -> */,
/* pos 041f: 623 */ 0xF4 /* 't' -> */,
/* pos 0420: 624 */ 0xE9 /* 'i' -> */,
/* pos 0421: 625 */ 0xE3 /* 'c' -> */,
/* pos 0422: 626 */ 0xE1 /* 'a' -> */,
/* pos 0423: 627 */ 0xF4 /* 't' -> */,
/* pos 0424: 628 */ 0xE5 /* 'e' -> */,
/* pos 0425: 629 */ 0xBA /* ':' -> */,
/* pos 0426: 630 */ 0x00, 0x48 /* - terminal marker 72 - */,
/* pos 0428: 631 */ 0xF4 /* 't' -> */,
/* pos 0429: 632 */ 0xE3 /* 'c' -> */,
/* pos 042a: 633 */ 0xE8 /* 'h' -> */,
/* pos 042b: 634 */ 0x00, 0x49 /* - terminal marker 73 - */,
/* pos 042d: 635 */ 0xF4 /* 't' -> */,
/* pos 042e: 636 */ 0x00, 0x4A /* - terminal marker 74 - */,
/* pos 0430: 637 */ 0xEC /* 'l' -> */,
/* pos 0431: 638 */ 0xE5 /* 'e' -> */,
/* pos 0432: 639 */ 0xF4 /* 't' -> */,
/* pos 0433: 640 */ 0xE5 /* 'e' -> */,
/* pos 0434: 641 */ 0x00, 0x4B /* - terminal marker 75 - */,
/* pos 0436: 642 */ 0xE9 /* 'i' -> */,
/* pos 0437: 643 */ 0xAD /* '-' -> */,
/* pos 0438: 644 */ 0xE1 /* 'a' -> */,
/* pos 0439: 645 */ 0xF2 /* 'r' -> */,
/* pos 043a: 646 */ 0xE7 /* 'g' -> */,
/* pos 043b: 647 */ 0xF3 /* 's' -> */,
/* pos 043c: 648 */ 0x00, 0x4C /* - terminal marker 76 - */,
/* pos 043e: 649 */ 0x00, 0x4D /* - terminal marker 77 - */,
/* pos 0440: 650 */ 0xAD /* '-' -> */,
/* pos 0441: 651 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0448 state 652) */,
0x66 /* 'f' */, 0x10, 0x00 /* (to 0x0454 state 662) */,
0x08, /* fail */
/* pos 0448: 652 */ 0xE5 /* 'e' -> */,
/* pos 0449: 653 */ 0xE1 /* 'a' -> */,
/* pos 044a: 654 */ 0xEC /* 'l' -> */,
/* pos 044b: 655 */ 0xAD /* '-' -> */,
/* pos 044c: 656 */ 0xE9 /* 'i' -> */,
/* pos 044d: 657 */ 0xF0 /* 'p' -> */,
/* pos 044e: 658 */ 0xBA /* ':' -> */,
/* pos 044f: 659 */ 0x00, 0x4E /* - terminal marker 78 - */,
/* pos 0451: 660 */ 0xA0 /* ' ' -> */,
/* pos 0452: 661 */ 0x00, 0x4F /* - terminal marker 79 - */,
/* pos 0454: 662 */ 0xEF /* 'o' -> */,
/* pos 0455: 663 */ 0xF2 /* 'r' -> */,
/* pos 0456: 664 */ 0xF7 /* 'w' -> */,
/* pos 0457: 665 */ 0xE1 /* 'a' -> */,
/* pos 0458: 666 */ 0xF2 /* 'r' -> */,
/* pos 0459: 667 */ 0xE4 /* 'd' -> */,
/* pos 045a: 668 */ 0xE5 /* 'e' -> */,
/* pos 045b: 669 */ 0xE4 /* 'd' -> */,
/* pos 045c: 670 */ 0xAD /* '-' -> */,
/* pos 045d: 671 */ 0xE6 /* 'f' -> */,
/* pos 045e: 672 */ 0xEF /* 'o' -> */,
/* pos 045f: 673 */ 0xF2 /* 'r' -> */,
/* pos 0460: 674 */ 0x00, 0x50 /* - terminal marker 80 - */,
/* pos 0462: 675 */ 0x00, 0x51 /* - terminal marker 81 - */,
/* pos 0464: 676 */ 0xE1 /* 'a' -> */,
/* pos 0465: 677 */ 0xE4 /* 'd' -> */,
/* pos 0466: 678 */ 0xA0 /* ' ' -> */,
/* pos 0467: 679 */ 0x00, 0x52 /* - terminal marker 82 - */,
/* pos 0469: 680 */ 0xBA /* ':' -> */,
/* pos 046a: 681 */ 0x00, 0x53 /* - terminal marker 83 - */,
/* total size 1132 bytes */

3048
thirdparty/lws/libwebsockets.c vendored Normal file

File diff suppressed because it is too large Load Diff

5790
thirdparty/lws/libwebsockets.h vendored Normal file

File diff suppressed because it is too large Load Diff

166
thirdparty/lws/lws_config.h vendored Normal file
View File

@ -0,0 +1,166 @@
/* lws_config.h Generated from lws_config.h.in */
#include "lws_config_private.h"
#ifndef NDEBUG
#ifndef _DEBUG
#define _DEBUG
#endif
#endif
#define LWS_INSTALL_DATADIR "/usr/local/share"
/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
* LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
/* #undef USE_WOLFSSL */
/* Also define to 1 (in addition to USE_WOLFSSL) when using the
(older) CyaSSL library */
/* #undef USE_OLD_CYASSL */
/* #undef LWS_WITH_BORINGSSL */
/* #undef LWS_WITH_MBEDTLS */
/* #undef LWS_WITH_POLARSSL */
/* #undef LWS_WITH_ESP8266 */
/* #undef LWS_WITH_ESP32 */
/* #undef LWS_WITH_PLUGINS */
/* #undef LWS_WITH_NO_LOGS */
#ifndef DEBUG_ENABLED
#define LWS_WITH_NO_LOGS
#endif
/* The Libwebsocket version */
#define LWS_LIBRARY_VERSION "2.4.1"
#define LWS_LIBRARY_VERSION_MAJOR 2
#define LWS_LIBRARY_VERSION_MINOR 4
#define LWS_LIBRARY_VERSION_PATCH 1
/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH
/* The current git commit hash that we're building from */
#define LWS_BUILD_HASH "55f97b7806e07db2d4c8a158172cd309d0faf450"
/* Build with OpenSSL support */
#define LWS_OPENSSL_SUPPORT
/* The client should load and trust CA root certs it finds in the OS */
#define LWS_SSL_CLIENT_USE_OS_CA_CERTS
/* Sets the path where the client certs should be installed. */
#define LWS_OPENSSL_CLIENT_CERTS "../share"
/* Turn off websocket extensions */
/* #undef LWS_NO_EXTENSIONS */
/* Enable libev io loop */
/* #undef LWS_WITH_LIBEV */
#undef LWS_WITH_LIBEV
/* Enable libuv io loop */
/* #undef LWS_WITH_LIBUV */
#undef LWS_WITH_LIBUV
/* Enable libevent io loop */
/* #undef LWS_WITH_LIBEVENT */
#undef LWS_WITH_LIBEVENT
/* Build with support for ipv6 */
/* #undef LWS_WITH_IPV6 */
/* Build with support for UNIX domain socket */
/* #undef LWS_WITH_UNIX_SOCK */
#ifdef WINDOWS_ENABLED
#undef LWS_USE_UNIX_SOCK
#endif
/* Build with support for HTTP2 */
/* #undef LWS_WITH_HTTP2 */
/* Turn on latency measuring code */
/* #undef LWS_LATENCY */
/* Don't build the daemonizeation api */
#define LWS_NO_DAEMONIZE
/* Build without server support */
/* #undef LWS_NO_SERVER */
/* Build without client support */
/* #undef LWS_NO_CLIENT */
/* If we should compile with MinGW support */
/* #undef LWS_MINGW_SUPPORT */
/* Use the BSD getifaddrs that comes with libwebsocket, for uclibc support */
/* #undef LWS_BUILTIN_GETIFADDRS */
/* use SHA1() not internal libwebsockets_SHA1 */
/* #undef LWS_SHA1_USE_OPENSSL_NAME */
/* SSL server using ECDH certificate */
/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */
#define LWS_HAVE_SSL_CTX_set1_param
#define LWS_HAVE_X509_VERIFY_PARAM_set1_host
/* #undef LWS_HAVE_RSA_SET0_KEY */
/* #undef LWS_HAVE_UV_VERSION_H */
/* CGI apis */
/* #undef LWS_WITH_CGI */
/* whether the Openssl is recent enough, and / or built with, ecdh */
#define LWS_HAVE_OPENSSL_ECDH_H
/* HTTP Proxy support */
/* #undef LWS_WITH_HTTP_PROXY */
/* HTTP Ranges support */
#define LWS_WITH_RANGES
/* Http access log support */
/* #undef LWS_WITH_ACCESS_LOG */
/* #undef LWS_WITH_SERVER_STATUS */
/* #undef LWS_WITH_STATEFUL_URLDECODE */
/* #undef LWS_WITH_PEER_LIMITS */
/* Maximum supported service threads */
#define LWS_MAX_SMP 1
/* Lightweight JSON Parser */
/* #undef LWS_WITH_LEJP */
/* SMTP */
/* #undef LWS_WITH_SMTP */
/* OPTEE */
/* #undef LWS_PLAT_OPTEE */
/* ZIP FOPS */
#define LWS_WITH_ZIP_FOPS
#define LWS_HAVE_STDINT_H
/* #undef LWS_AVOID_SIGPIPE_IGN */
/* #undef LWS_FALLBACK_GETHOSTBYNAME */
/* #undef LWS_WITH_STATS */
/* #undef LWS_WITH_SOCKS5 */
/* #undef LWS_HAVE_SYS_CAPABILITY_H */
/* #undef LWS_HAVE_LIBCAP */
#define LWS_HAVE_ATOLL
/* #undef LWS_HAVE__ATOI64 */
/* #undef LWS_HAVE__STAT32I64 */
/* OpenSSL various APIs */
/* #undef LWS_HAVE_TLS_CLIENT_METHOD */
#define LWS_HAVE_TLSV1_2_CLIENT_METHOD
#define LWS_HAVE_SSL_SET_INFO_CALLBACK
#define LWS_HAS_INTPTR_T

143
thirdparty/lws/lws_config_private.h vendored Normal file
View File

@ -0,0 +1,143 @@
/* lws_config_private.h.in. Private compilation options. */
#ifndef DEBUG_ENABLED
#define NDEBUG
#endif
#ifndef NDEBUG
#ifndef _DEBUG
#define _DEBUG
#endif
#endif
/* Define to 1 to use CyaSSL as a replacement for OpenSSL.
* LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
/* #undef USE_CYASSL */
/* Define to 1 if you have the `bzero' function. */
#define LWS_HAVE_BZERO
/* Windows has no bzero function */
#ifdef WINDOWS_ENABLED
#undef LWS_HAVE_BZERO
#endif
/* Define to 1 if you have the <dlfcn.h> header file. */
#define LWS_HAVE_DLFCN_H
/* Define to 1 if you have the <fcntl.h> header file. */
#define LWS_HAVE_FCNTL_H
#ifdef NO_FCNTL
#undef LWS_HAVE_FCNTL_H
#endif
/* Define to 1 if you have the `fork' function. */
#define LWS_HAVE_FORK
/* Define to 1 if you have the `getenv function. */
#define LWS_HAVE_GETENV
/* Define to 1 if you have the <in6addr.h> header file. */
/* #undef LWS_HAVE_IN6ADDR_H */
/* Define to 1 if you have the <inttypes.h> header file. */
#define LWS_HAVE_INTTYPES_H
/* Define to 1 if you have the `ssl' library (-lssl). */
/* #undef LWS_HAVE_LIBSSL */
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
to 0 otherwise. */
#define LWS_HAVE_MALLOC
/* Define to 1 if you have the <memory.h> header file. */
#define LWS_HAVE_MEMORY_H
/* Define to 1 if you have the `memset' function. */
#define LWS_HAVE_MEMSET
/* Define to 1 if you have the <netinet/in.h> header file. */
#define LWS_HAVE_NETINET_IN_H
/* Define to 1 if your system has a GNU libc compatible `realloc' function,
and to 0 otherwise. */
#define LWS_HAVE_REALLOC
/* Define to 1 if you have the `socket' function. */
#define LWS_HAVE_SOCKET
/* Define to 1 if you have the <stdint.h> header file. */
#define LWS_HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#define LWS_HAVE_STDLIB_H
/* Define to 1 if you have the `strerror' function. */
#define LWS_HAVE_STRERROR
/* Define to 1 if you have the <strings.h> header file. */
#define LWS_HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#define LWS_HAVE_STRING_H
/* Define to 1 if you have the <sys/prctl.h> header file. */
#define LWS_HAVE_SYS_PRCTL_H
#if defined(OSX_ENABLED) || defined(IPHONE_ENABLED)
#undef LWS_HAVE_SYS_PRCTL_H
#endif
/* Define to 1 if you have the <sys/socket.h> header file. */
#define LWS_HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/sockio.h> header file. */
/* #undef LWS_HAVE_SYS_SOCKIO_H */
/* Define to 1 if you have the <sys/stat.h> header file. */
#define LWS_HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#define LWS_HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#define LWS_HAVE_UNISTD_H
/* Define to 1 if you have the `vfork' function. */
#define LWS_HAVE_VFORK
/* Define to 1 if you have the <vfork.h> header file. */
/* #undef LWS_HAVE_VFORK_H */
/* Define to 1 if `fork' works. */
#define LWS_HAVE_WORKING_FORK
/* Define to 1 if `vfork' works. */
#define LWS_HAVE_WORKING_VFORK
/* Define to 1 if execvpe() exists */
#define LWS_HAVE_EXECVPE
/* Define to 1 if you have the <zlib.h> header file. */
#define LWS_HAVE_ZLIB_H
#define LWS_HAVE_GETLOADAVG
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR // We're not using libtool
/* Define to rpl_malloc if the replacement function should be used. */
/* #undef malloc */
/* Define to rpl_realloc if the replacement function should be used. */
/* #undef realloc */
/* Define to 1 if we have getifaddrs */
#define LWS_HAVE_GETIFADDRS
#if defined(ANDROID_ENABLED)
#undef LWS_HAVE_GETIFADDRS
#define LWS_BUILTIN_GETIFADDRS
#endif
/* Define if the inline keyword doesn't exist. */
/* #undef inline */

272
thirdparty/lws/minilex.c vendored Normal file
View File

@ -0,0 +1,272 @@
/*
* minilex.c
*
* High efficiency lexical state parser
*
* Copyright (C)2011-2014 Andy Green <andy@warmcat.com>
*
* Licensed under LGPL2
*
* Usage: gcc minilex.c -o minilex && ./minilex > lextable.h
*
* Run it twice to test parsing on the generated table on stderr
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lextable-strings.h"
/*
* 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[] = {
#include "lextable.h"
};
#define PARALLEL 30
struct state {
char c[PARALLEL];
int state[PARALLEL];
int count;
int bytepos;
int real_pos;
};
struct state state[1000];
int next = 1;
#define FAIL_CHAR 0x08
int lextable_decode(int pos, char c)
{
while (1) {
if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
if ((lextable[pos] & 0x7f) != c)
return -1;
/* fall thru */
pos++;
if (lextable[pos] == FAIL_CHAR)
return -1;
return pos;
} else { /* b7 = 0, end or 3-byte */
if (lextable[pos] < FAIL_CHAR) /* terminal marker */
return pos;
if (lextable[pos] == c) /* goto */
return pos + (lextable[pos + 1]) +
(lextable[pos + 2] << 8);
/* fall thru goto */
pos += 3;
/* continue */
}
}
}
int main(void)
{
int n = 0;
int m = 0;
int prev;
char c;
int walk;
int saw;
int y;
int j;
int pos = 0;
while (n < sizeof(set) / sizeof(set[0])) {
m = 0;
walk = 0;
prev = 0;
if (set[n][0] == '\0') {
n++;
continue;
}
while (set[n][m]) {
saw = 0;
for (y = 0; y < state[walk].count; y++)
if (state[walk].c[y] == set[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] = set[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);
/*
* Try to parse every legal input string
*/
for (n = 0; n < sizeof(set) / sizeof(set[0]); n++) {
walk = 0;
m = 0;
y = -1;
if (set[n][0] == '\0')
continue;
fprintf(stderr, " trying '%s'\n", set[n]);
while (set[n][m]) {
walk = lextable_decode(walk, set[n][m]);
if (walk < 0) {
fprintf(stderr, "failed\n");
return 3;
}
if (lextable[walk] < FAIL_CHAR) {
y = (lextable[walk] << 8) + lextable[walk + 1];
break;
}
m++;
}
if (y != n) {
fprintf(stderr, "decode failed %d\n", y);
return 4;
}
}
fprintf(stderr, "All decode OK\n");
return 0;
}

206
thirdparty/lws/misc/base64-decode.c vendored Normal file
View File

@ -0,0 +1,206 @@
/*
* This code originally came from here
*
* http://base64.sourceforge.net/b64.c
*
* with the following license:
*
* LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
*
* 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.
*
* VERSION HISTORY:
* Bob Trower 08/04/01 -- Create Version 0.00.00B
*
* I cleaned it up quite a bit to match the (linux kernel) style of the rest
* of libwebsockets; this version is under LGPL2.1 + SLE like the rest of lws
* since he explicitly allows sublicensing, but I give the URL above so you can
* get the original with Bob's super-liberal terms directly if you prefer.
*/
#include <stdio.h>
#include <string.h>
#include "private-libwebsockets.h"
static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
"$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
LWS_VISIBLE int
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
{
unsigned char triple[3];
int i;
int len;
int line = 0;
int done = 0;
while (in_len) {
len = 0;
for (i = 0; i < 3; i++) {
if (in_len) {
triple[i] = *in++;
len++;
in_len--;
} else
triple[i] = 0;
}
if (done + 4 >= out_size)
return -1;
*out++ = encode[triple[0] >> 2];
*out++ = encode[((triple[0] & 0x03) << 4) |
((triple[1] & 0xf0) >> 4)];
*out++ = (len > 1 ? encode[((triple[1] & 0x0f) << 2) |
((triple[2] & 0xc0) >> 6)] : '=');
*out++ = (len > 2 ? encode[triple[2] & 0x3f] : '=');
done += 4;
line += 4;
}
if (done + 1 >= out_size)
return -1;
*out++ = '\0';
return done;
}
/*
* returns length of decoded string in out, or -1 if out was too small
* according to out_size
*/
LWS_VISIBLE int
lws_b64_decode_string(const char *in, char *out, int out_size)
{
int len, i, c = 0, done = 0;
unsigned char v, quad[4];
while (*in) {
len = 0;
for (i = 0; i < 4 && *in; i++) {
v = 0;
c = 0;
while (*in && !v) {
c = v = *in++;
v = (v < 43 || v > 122) ? 0 : decode[v - 43];
if (v)
v = (v == '$') ? 0 : v - 61;
}
if (c) {
len++;
if (v)
quad[i] = v - 1;
} else
quad[i] = 0;
}
if (out_size < (done + len - 1))
/* out buffer is too small */
return -1;
/*
* "The '==' sequence indicates that the last group contained
* only one byte, and '=' indicates that it contained two
* bytes." (wikipedia)
*/
if (!*in && c == '=')
len--;
if (len >= 2)
*out++ = quad[0] << 2 | quad[1] >> 4;
if (len >= 3)
*out++ = quad[1] << 4 | quad[2] >> 2;
if (len >= 4)
*out++ = ((quad[2] << 6) & 0xc0) | quad[3];
done += len - 1;
}
if (done + 1 >= out_size)
return -1;
*out = '\0';
return done;
}
#if 0
int
lws_b64_selftest(void)
{
char buf[64];
unsigned int n, r = 0;
unsigned int test;
/* examples from https://en.wikipedia.org/wiki/Base64 */
static const char * const plaintext[] = {
"any carnal pleasure.",
"any carnal pleasure",
"any carnal pleasur",
"any carnal pleasu",
"any carnal pleas",
"Admin:kloikloi"
};
static const char * const coded[] = {
"YW55IGNhcm5hbCBwbGVhc3VyZS4=",
"YW55IGNhcm5hbCBwbGVhc3VyZQ==",
"YW55IGNhcm5hbCBwbGVhc3Vy",
"YW55IGNhcm5hbCBwbGVhc3U=",
"YW55IGNhcm5hbCBwbGVhcw==",
"QWRtaW46a2xvaWtsb2k="
};
for (test = 0; test < sizeof plaintext / sizeof(plaintext[0]); test++) {
buf[sizeof(buf) - 1] = '\0';
n = lws_b64_encode_string(plaintext[test],
strlen(plaintext[test]), buf, sizeof buf);
if (n != strlen(coded[test]) || strcmp(buf, coded[test])) {
lwsl_err("Failed lws_b64 encode selftest "
"%d result '%s' %d\n", test, buf, n);
r = -1;
}
buf[sizeof(buf) - 1] = '\0';
n = lws_b64_decode_string(coded[test], buf, sizeof buf);
if (n != strlen(plaintext[test]) ||
strcmp(buf, plaintext[test])) {
lwsl_err("Failed lws_b64 decode selftest "
"%d result '%s' / '%s', %d / %d\n",
test, buf, plaintext[test], n, strlen(plaintext[test]));
r = -1;
}
}
lwsl_notice("Base 64 selftests passed\n");
return r;
}
#endif

270
thirdparty/lws/misc/getifaddrs.c vendored Normal file
View File

@ -0,0 +1,270 @@
/*
* Copyright (c) 2000 - 2001 Kungliga Tekniska H<EFBFBD>gskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* originally downloaded from
*
* http://ftp.uninett.no/pub/OpenBSD/src/kerberosV/src/lib/roken/getifaddrs.c
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "private-libwebsockets.h"
#ifdef LWS_HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#ifdef LWS_HAVE_NETINET_IN6_VAR_H
#include <netinet/in6_var.h>
#endif
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
#include "getifaddrs.h"
static int
getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
size_t ifreq_sz)
{
int ret;
int fd;
size_t buf_size;
char *buf;
struct ifconf ifconf;
char *p;
size_t sz;
struct sockaddr sa_zero;
struct ifreq *ifr;
struct ifaddrs *start, **end = &start;
buf = NULL;
memset(&sa_zero, 0, sizeof(sa_zero));
fd = socket(af, SOCK_DGRAM, 0);
if (fd < 0)
return -1;
buf_size = 8192;
for (;;) {
buf = lws_zalloc(buf_size, "getifaddrs2");
if (buf == NULL) {
ret = ENOMEM;
goto error_out;
}
ifconf.ifc_len = buf_size;
ifconf.ifc_buf = buf;
/*
* Solaris returns EINVAL when the buffer is too small.
*/
if (ioctl(fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) {
ret = errno;
goto error_out;
}
/*
* Can the difference between a full and a overfull buf
* be determined?
*/
if (ifconf.ifc_len < (int)buf_size)
break;
lws_free(buf);
buf_size *= 2;
}
for (p = ifconf.ifc_buf; p < ifconf.ifc_buf + ifconf.ifc_len; p += sz) {
struct ifreq ifreq;
struct sockaddr *sa;
size_t salen;
ifr = (struct ifreq *)p;
sa = &ifr->ifr_addr;
sz = ifreq_sz;
salen = sizeof(struct sockaddr);
#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN
salen = sa->sa_len;
sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len);
#endif
#ifdef SA_LEN
salen = SA_LEN(sa);
sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa));
#endif
memset(&ifreq, 0, sizeof(ifreq));
memcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name));
if (ioctl(fd, siocgifflags, &ifreq) < 0) {
ret = errno;
goto error_out;
}
*end = lws_malloc(sizeof(**end), "getifaddrs");
(*end)->ifa_next = NULL;
(*end)->ifa_name = strdup(ifr->ifr_name);
(*end)->ifa_flags = ifreq.ifr_flags;
(*end)->ifa_addr = lws_malloc(salen, "getifaddrs");
memcpy((*end)->ifa_addr, sa, salen);
(*end)->ifa_netmask = NULL;
#if 0
/* fix these when we actually need them */
if (ifreq.ifr_flags & IFF_BROADCAST) {
(*end)->ifa_broadaddr =
lws_malloc(sizeof(ifr->ifr_broadaddr), "getifaddrs");
memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
sizeof(ifr->ifr_broadaddr));
} else if (ifreq.ifr_flags & IFF_POINTOPOINT) {
(*end)->ifa_dstaddr =
lws_malloc(sizeof(ifr->ifr_dstaddr), "getifaddrs");
memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
sizeof(ifr->ifr_dstaddr));
} else
(*end)->ifa_dstaddr = NULL;
#else
(*end)->ifa_dstaddr = NULL;
#endif
(*end)->ifa_data = NULL;
end = &(*end)->ifa_next;
}
*ifap = start;
close(fd);
lws_free(buf);
return 0;
error_out:
close(fd);
lws_free(buf);
errno = ret;
return -1;
}
int
getifaddrs(struct ifaddrs **ifap)
{
int ret = -1;
errno = ENXIO;
#if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS)
if (ret)
ret = getifaddrs2(ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS,
sizeof(struct in6_ifreq));
#endif
#if defined(LWS_HAVE_IPV6) && defined(SIOCGIFCONF)
if (ret)
ret = getifaddrs2(ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS,
sizeof(struct ifreq));
#endif
#if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS)
if (ret)
ret = getifaddrs2(ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
sizeof(struct ifreq));
#endif
return ret;
}
void
freeifaddrs(struct ifaddrs *ifp)
{
struct ifaddrs *p, *q;
for (p = ifp; p; ) {
lws_free(p->ifa_name);
lws_free(p->ifa_addr);
lws_free(p->ifa_dstaddr);
lws_free(p->ifa_netmask);
lws_free(p->ifa_data);
q = p;
p = p->ifa_next;
lws_free(q);
}
}
#ifdef TEST
void
print_addr(const char *s, struct sockaddr *sa)
{
int i;
printf(" %s=%d/", s, sa->sa_family);
#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN
for (i = 0;
i < sa->sa_len - ((lws_intptr_t)sa->sa_data - (lws_intptr_t)&sa->sa_family); i++)
printf("%02x", ((unsigned char *)sa->sa_data)[i]);
#else
for (i = 0; i < sizeof(sa->sa_data); i++)
printf("%02x", ((unsigned char *)sa->sa_data)[i]);
#endif
printf("\n");
}
void
print_ifaddrs(struct ifaddrs *x)
{
struct ifaddrs *p;
for (p = x; p; p = p->ifa_next) {
printf("%s\n", p->ifa_name);
printf(" flags=%x\n", p->ifa_flags);
if (p->ifa_addr)
print_addr("addr", p->ifa_addr);
if (p->ifa_dstaddr)
print_addr("dstaddr", p->ifa_dstaddr);
if (p->ifa_netmask)
print_addr("netmask", p->ifa_netmask);
printf(" %p\n", p->ifa_data);
}
}
int
main()
{
struct ifaddrs *a = NULL, *b;
getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
sizeof(struct ifreq));
print_ifaddrs(a);
printf("---\n");
getifaddrs(&b);
print_ifaddrs(b);
return 0;
}
#endif

80
thirdparty/lws/misc/getifaddrs.h vendored Normal file
View File

@ -0,0 +1,80 @@
#ifndef LWS_HAVE_GETIFADDRS
#define LWS_HAVE_GETIFADDRS 0
#endif
#if LWS_HAVE_GETIFADDRS
#include <sys/types.h>
#include <ifaddrs.h>
#else
#ifdef __cplusplus
extern "C" {
#endif
/*
* Copyright (c) 2000 Kungliga Tekniska H<EFBFBD>gskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* $KTH: ifaddrs.hin,v 1.3 2000/12/11 00:01:13 assar Exp $ */
#ifndef ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791
#define ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791
/*
* the interface is defined in terms of the fields below, and this is
* sometimes #define'd, so there seems to be no simple way of solving
* this and this seemed the best. */
#undef ifa_dstaddr
struct ifaddrs {
struct ifaddrs *ifa_next;
char *ifa_name;
unsigned int ifa_flags;
struct sockaddr *ifa_addr;
struct sockaddr *ifa_netmask;
struct sockaddr *ifa_dstaddr;
void *ifa_data;
};
#ifndef ifa_broadaddr
#define ifa_broadaddr ifa_dstaddr
#endif
int getifaddrs(struct ifaddrs **);
void freeifaddrs(struct ifaddrs *);
#endif /* __ifaddrs_h__ */
#ifdef __cplusplus
}
#endif
#endif

709
thirdparty/lws/misc/lejp.c vendored Normal file
View File

@ -0,0 +1,709 @@
/*
* Lightweight Embedded JSON Parser
*
* Copyright (C) 2013 Andy Green <andy@warmcat.com>
* This code is licensed under LGPL 2.1
* http://www.gnu.org/licenses/lgpl-2.1.html
*/
#include <string.h>
#include "lejp.h"
#include <stdio.h>
/**
* lejp_construct - prepare a struct lejp_ctx for use
*
* \param ctx: pointer to your struct lejp_ctx
* \param callback: your user callback which will received parsed tokens
* \param user: optional user data pointer untouched by lejp
* \param paths: your array of name elements you are interested in
* \param count_paths: ARRAY_SIZE() of @paths
*
* Prepares your context struct for use with lejp
*/
void
lejp_construct(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
const char * const *paths, unsigned char count_paths)
{
ctx->st[0].s = 0;
ctx->st[0].p = 0;
ctx->st[0].i = 0;
ctx->st[0].b = 0;
ctx->sp = 0;
ctx->ipos = 0;
ctx->ppos = 0;
ctx->path_match = 0;
ctx->path[0] = '\0';
ctx->callback = callback;
ctx->user = user;
ctx->paths = paths;
ctx->count_paths = count_paths;
ctx->line = 1;
ctx->callback(ctx, LEJPCB_CONSTRUCTED);
}
/**
* lejp_destruct - retire a previously constructed struct lejp_ctx
*
* \param ctx: pointer to your struct lejp_ctx
*
* lejp does not perform any allocations, but since your user code might, this
* provides a one-time LEJPCB_DESTRUCTED callback at destruction time where
* you can clean up in your callback.
*/
void
lejp_destruct(struct lejp_ctx *ctx)
{
/* no allocations... just let callback know what it happening */
ctx->callback(ctx, LEJPCB_DESTRUCTED);
}
/**
* lejp_change_callback - switch to a different callback from now on
*
* \param ctx: pointer to your struct lejp_ctx
* \param callback: your user callback which will received parsed tokens
*
* This tells the old callback it was destroyed, in case you want to take any
* action because that callback "lost focus", then changes to the new
* callback and tells it first that it was constructed, and then started.
*
* Changing callback is a cheap and powerful trick to split out handlers
* according to information earlier in the parse. For example you may have
* a JSON pair "schema" whose value defines what can be expected for the rest
* of the JSON. Rather than having one huge callback for all cases, you can
* have an initial one looking for "schema" which then calls
* lejp_change_callback() to a handler specific for the schema.
*
* Notice that afterwards, you need to construct the context again anyway to
* parse another JSON object, and the callback is reset then to the main,
* schema-interpreting one. The construction action is very lightweight.
*/
void
lejp_change_callback(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason))
{
ctx->callback(ctx, LEJPCB_DESTRUCTED);
ctx->callback = callback;
ctx->callback(ctx, LEJPCB_CONSTRUCTED);
ctx->callback(ctx, LEJPCB_START);
}
static void
lejp_check_path_match(struct lejp_ctx *ctx)
{
const char *p, *q;
int n;
/* we only need to check if a match is not active */
for (n = 0; !ctx->path_match && n < ctx->count_paths; n++) {
ctx->wildcount = 0;
p = ctx->path;
q = ctx->paths[n];
while (*p && *q) {
if (*q != '*') {
if (*p != *q)
break;
p++;
q++;
continue;
}
ctx->wild[ctx->wildcount++] = p - ctx->path;
q++;
/*
* if * has something after it, match to .
* if ends with *, eat everything.
* This implies match sequences must be ordered like
* x.*.*
* x.*
* if both options are possible
*/
while (*p && (*p != '.' || !*q))
p++;
}
if (*p || *q)
continue;
ctx->path_match = n + 1;
ctx->path_match_len = ctx->ppos;
return;
}
if (!ctx->path_match)
ctx->wildcount = 0;
}
int
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
{
int n;
if (wildcard >= ctx->wildcount || !len)
return 0;
n = ctx->wild[wildcard];
while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.'))
*dest++ = ctx->path[n++];
*dest = '\0';
n++;
return n - ctx->wild[wildcard];
}
/**
* lejp_parse - interpret some more incoming data incrementally
*
* \param ctx: previously constructed parsing context
* \param json: char buffer with the new data to interpret
* \param len: amount of data in the buffer
*
* Because lejp is a stream parser, it incrementally parses as new data
* becomes available, maintaining all state in the context struct. So an
* incomplete JSON is a normal situation, getting you a LEJP_CONTINUE
* return, signalling there's no error but to call again with more data when
* it comes to complete the parsing. Successful parsing completes with a
* 0 or positive integer indicating how much of the last input buffer was
* unused.
*/
int
lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
{
unsigned char c, n, s, ret = LEJP_REJECT_UNKNOWN;
static const char esc_char[] = "\"\\/bfnrt";
static const char esc_tran[] = "\"\\/\b\f\n\r\t";
static const char tokens[] = "rue alse ull ";
if (!ctx->sp && !ctx->ppos)
ctx->callback(ctx, LEJPCB_START);
while (len--) {
c = *json++;
s = ctx->st[ctx->sp].s;
/* skip whitespace unless we should care */
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '#') {
if (c == '\n') {
ctx->line++;
ctx->st[ctx->sp].s &= ~LEJP_FLAG_WS_COMMENTLINE;
}
if (!(s & LEJP_FLAG_WS_KEEP)) {
if (c == '#')
ctx->st[ctx->sp].s |=
LEJP_FLAG_WS_COMMENTLINE;
continue;
}
}
if (ctx->st[ctx->sp].s & LEJP_FLAG_WS_COMMENTLINE)
continue;
switch (s) {
case LEJP_IDLE:
if (c != '{') {
ret = LEJP_REJECT_IDLE_NO_BRACE;
goto reject;
}
if (ctx->callback(ctx, LEJPCB_OBJECT_START)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_MEMBERS;
break;
case LEJP_MEMBERS:
if (c == '}') {
ctx->st[ctx->sp].s = LEJP_IDLE;
ret = LEJP_REJECT_MEMBERS_NO_CLOSE;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_M_P;
goto redo_character;
case LEJP_M_P:
if (c != '\"') {
ret = LEJP_REJECT_MP_NO_OPEN_QUOTE;
goto reject;
}
/* push */
ctx->st[ctx->sp].s = LEJP_MP_DELIM;
c = LEJP_MP_STRING;
goto add_stack_level;
case LEJP_MP_STRING:
if (c == '\"') {
if (!ctx->sp) {
ret = LEJP_REJECT_MP_STRING_UNDERRUN;
goto reject;
}
if (ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) {
ctx->buf[ctx->npos] = '\0';
if (ctx->callback(ctx,
LEJPCB_VAL_STR_END) < 0) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
}
/* pop */
ctx->sp--;
break;
}
if (c == '\\') {
ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC;
break;
}
if (c < ' ') {/* "control characters" not allowed */
ret = LEJP_REJECT_MP_ILLEGAL_CTRL;
goto reject;
}
goto emit_string_char;
case LEJP_MP_STRING_ESC:
if (c == 'u') {
ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC_U1;
ctx->uni = 0;
break;
}
for (n = 0; n < sizeof(esc_char); n++) {
if (c != esc_char[n])
continue;
/* found it */
c = esc_tran[n];
ctx->st[ctx->sp].s = LEJP_MP_STRING;
goto emit_string_char;
}
ret = LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC;
/* illegal escape char */
goto reject;
case LEJP_MP_STRING_ESC_U1:
case LEJP_MP_STRING_ESC_U2:
case LEJP_MP_STRING_ESC_U3:
case LEJP_MP_STRING_ESC_U4:
ctx->uni <<= 4;
if (c >= '0' && c <= '9')
ctx->uni |= c - '0';
else
if (c >= 'a' && c <= 'f')
ctx->uni = c - 'a' + 10;
else
if (c >= 'A' && c <= 'F')
ctx->uni = c - 'A' + 10;
else {
ret = LEJP_REJECT_ILLEGAL_HEX;
goto reject;
}
ctx->st[ctx->sp].s++;
switch (s) {
case LEJP_MP_STRING_ESC_U2:
if (ctx->uni < 0x08)
break;
/*
* 0x08-0xff (0x0800 - 0xffff)
* emit 3-byte UTF-8
*/
c = 0xe0 | ((ctx->uni >> 4) & 0xf);
goto emit_string_char;
case LEJP_MP_STRING_ESC_U3:
if (ctx->uni >= 0x080) {
/*
* 0x080 - 0xfff (0x0800 - 0xffff)
* middle 3-byte seq
* send ....XXXXXX..
*/
c = 0x80 | ((ctx->uni >> 2) & 0x3f);
goto emit_string_char;
}
if (ctx->uni < 0x008)
break;
/*
* 0x008 - 0x7f (0x0080 - 0x07ff)
* start 2-byte seq
*/
c = 0xc0 | (ctx->uni >> 2);
goto emit_string_char;
case LEJP_MP_STRING_ESC_U4:
if (ctx->uni >= 0x0080)
/* end of 2 or 3-byte seq */
c = 0x80 | (ctx->uni & 0x3f);
else
/* literal */
c = (unsigned char)ctx->uni;
ctx->st[ctx->sp].s = LEJP_MP_STRING;
goto emit_string_char;
default:
break;
}
break;
case LEJP_MP_DELIM:
if (c != ':') {
ret = LEJP_REJECT_MP_DELIM_MISSING_COLON;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_MP_VALUE;
ctx->path[ctx->ppos] = '\0';
lejp_check_path_match(ctx);
if (ctx->callback(ctx, LEJPCB_PAIR_NAME)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
case LEJP_MP_VALUE:
if (c >= '0' && c <= '9') {
ctx->npos = 0;
ctx->dcount = 0;
ctx->f = 0;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT;
goto redo_character;
}
switch (c) {
case'\"':
/* push */
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
c = LEJP_MP_STRING;
ctx->npos = 0;
ctx->buf[0] = '\0';
if (ctx->callback(ctx, LEJPCB_VAL_STR_START)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
goto add_stack_level;
case '{':
/* push */
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
c = LEJP_MEMBERS;
lejp_check_path_match(ctx);
if (ctx->callback(ctx, LEJPCB_OBJECT_START)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->path_match = 0;
goto add_stack_level;
case '[':
/* push */
ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END;
c = LEJP_MP_VALUE;
ctx->path[ctx->ppos++] = '[';
ctx->path[ctx->ppos++] = ']';
ctx->path[ctx->ppos] = '\0';
if (ctx->callback(ctx, LEJPCB_ARRAY_START)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->i[ctx->ipos++] = 0;
if (ctx->ipos > ARRAY_SIZE(ctx->i)) {
ret = LEJP_REJECT_MP_DELIM_ISTACK;
goto reject;
}
goto add_stack_level;
case 't': /* true */
ctx->uni = 0;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
break;
case 'f':
ctx->uni = 4;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
break;
case 'n':
ctx->uni = 4 + 5;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
break;
default:
ret = LEJP_REJECT_MP_DELIM_BAD_VALUE_START;
goto reject;
}
break;
case LEJP_MP_VALUE_NUM_INT:
if (!ctx->npos && c == '-') {
ctx->f |= LEJP_SEEN_MINUS;
goto append_npos;
}
if (ctx->dcount < 10 && c >= '0' && c <= '9') {
if (ctx->f & LEJP_SEEN_POINT)
ctx->f |= LEJP_SEEN_POST_POINT;
ctx->dcount++;
goto append_npos;
}
if (c == '.') {
if (ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
ctx->f |= LEJP_SEEN_POINT;
goto append_npos;
}
/*
* before exponent, if we had . we must have had at
* least one more digit
*/
if ((ctx->f &
(LEJP_SEEN_POINT | LEJP_SEEN_POST_POINT)) ==
LEJP_SEEN_POINT) {
ret = LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC;
goto reject;
}
if (c == 'e' || c == 'E') {
if (ctx->f & LEJP_SEEN_EXP) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
ctx->f |= LEJP_SEEN_EXP;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_EXP;
goto append_npos;
}
/* if none of the above, did we even have a number? */
if (!ctx->dcount) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
ctx->buf[ctx->npos] = '\0';
if (ctx->f & LEJP_SEEN_POINT) {
if (ctx->callback(ctx, LEJPCB_VAL_NUM_FLOAT)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
} else {
if (ctx->callback(ctx, LEJPCB_VAL_NUM_INT)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
}
/* then this is the post-number character, loop */
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
goto redo_character;
case LEJP_MP_VALUE_NUM_EXP:
ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT;
if (c >= '0' && c <= '9')
goto redo_character;
if (c == '+' || c == '-')
goto append_npos;
ret = LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP;
goto reject;
case LEJP_MP_VALUE_TOK: /* true, false, null */
if (c != tokens[ctx->uni]) {
ret = LEJP_REJECT_MP_VAL_TOK_UNKNOWN;
goto reject;
}
ctx->uni++;
if (tokens[ctx->uni] != ' ')
break;
switch (ctx->uni) {
case 3:
ctx->buf[0] = '1';
ctx->buf[1] = '\0';
if (ctx->callback(ctx, LEJPCB_VAL_TRUE)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
case 8:
ctx->buf[0] = '0';
ctx->buf[1] = '\0';
if (ctx->callback(ctx, LEJPCB_VAL_FALSE)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
case 12:
ctx->buf[0] = '\0';
if (ctx->callback(ctx, LEJPCB_VAL_NULL)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
}
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
break;
case LEJP_MP_COMMA_OR_END:
ctx->path[ctx->ppos] = '\0';
if (c == ',') {
/* increment this stack level's index */
ctx->st[ctx->sp].s = LEJP_M_P;
if (!ctx->sp) {
ctx->ppos = 0;
/*
* since we came back to root level,
* no path can still match
*/
ctx->path_match = 0;
break;
}
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
if (ctx->st[ctx->sp - 1].s != LEJP_MP_ARRAY_END)
break;
/* top level is definitely an array... */
if (ctx->ipos)
ctx->i[ctx->ipos - 1]++;
ctx->st[ctx->sp].s = LEJP_MP_VALUE;
break;
}
if (c == ']') {
if (!ctx->sp) {
ret = LEJP_REJECT_MP_C_OR_E_UNDERF;
goto reject;
}
/* pop */
ctx->sp--;
if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) {
ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY;
goto reject;
}
/* drop the path [n] bit */
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
/* do LEJP_MP_ARRAY_END processing */
goto redo_character;
}
if (c == '}') {
if (ctx->sp == 0) {
lejp_check_path_match(ctx);
if (ctx->callback(ctx, LEJPCB_OBJECT_END)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->callback(ctx, LEJPCB_COMPLETE);
/* done, return unused amount */
return len;
}
/* pop */
ctx->sp--;
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
lejp_check_path_match(ctx);
if (ctx->callback(ctx, LEJPCB_OBJECT_END)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
}
ret = LEJP_REJECT_MP_C_OR_E_NEITHER;
goto reject;
case LEJP_MP_ARRAY_END:
ctx->path[ctx->ppos] = '\0';
if (c == ',') {
/* increment this stack level's index */
if (ctx->ipos)
ctx->i[ctx->ipos - 1]++;
ctx->st[ctx->sp].s = LEJP_MP_VALUE;
if (ctx->sp)
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->path[ctx->ppos] = '\0';
break;
}
if (c != ']') {
ret = LEJP_REJECT_MP_ARRAY_END_MISSING;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
ctx->callback(ctx, LEJPCB_ARRAY_END);
break;
}
continue;
emit_string_char:
if (!ctx->sp || ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) {
/* assemble the string value into chunks */
ctx->buf[ctx->npos++] = c;
if (ctx->npos == sizeof(ctx->buf) - 1) {
if (ctx->callback(ctx, LEJPCB_VAL_STR_CHUNK)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->npos = 0;
}
continue;
}
/* name part of name:value pair */
ctx->path[ctx->ppos++] = c;
continue;
add_stack_level:
/* push on to the object stack */
if (ctx->ppos && ctx->st[ctx->sp].s != LEJP_MP_COMMA_OR_END &&
ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END)
ctx->path[ctx->ppos++] = '.';
ctx->st[ctx->sp].p = ctx->ppos;
ctx->st[ctx->sp].i = ctx->ipos;
if (++ctx->sp == ARRAY_SIZE(ctx->st)) {
ret = LEJP_REJECT_STACK_OVERFLOW;
goto reject;
}
ctx->path[ctx->ppos] = '\0';
ctx->st[ctx->sp].s = c;
ctx->st[ctx->sp].b = 0;
continue;
append_npos:
if (ctx->npos >= sizeof(ctx->buf)) {
ret = LEJP_REJECT_NUM_TOO_LONG;
goto reject;
}
ctx->buf[ctx->npos++] = c;
continue;
redo_character:
json--;
len++;
}
return LEJP_CONTINUE;
reject:
ctx->callback(ctx, LEJPCB_FAILED);
return ret;
}

232
thirdparty/lws/misc/lejp.h vendored Normal file
View File

@ -0,0 +1,232 @@
#include "libwebsockets.h"
struct lejp_ctx;
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
#endif
#define LEJP_FLAG_WS_KEEP 64
#define LEJP_FLAG_WS_COMMENTLINE 32
enum lejp_states {
LEJP_IDLE = 0,
LEJP_MEMBERS = 1,
LEJP_M_P = 2,
LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3,
LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4,
LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5,
LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6,
LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7,
LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8,
LEJP_MP_DELIM = 9,
LEJP_MP_VALUE = 10,
LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11,
LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12,
LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13,
LEJP_MP_COMMA_OR_END = 14,
LEJP_MP_ARRAY_END = 15,
};
enum lejp_reasons {
LEJP_CONTINUE = -1,
LEJP_REJECT_IDLE_NO_BRACE = -2,
LEJP_REJECT_MEMBERS_NO_CLOSE = -3,
LEJP_REJECT_MP_NO_OPEN_QUOTE = -4,
LEJP_REJECT_MP_STRING_UNDERRUN = -5,
LEJP_REJECT_MP_ILLEGAL_CTRL = -6,
LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7,
LEJP_REJECT_ILLEGAL_HEX = -8,
LEJP_REJECT_MP_DELIM_MISSING_COLON = -9,
LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10,
LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11,
LEJP_REJECT_MP_VAL_NUM_FORMAT = -12,
LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13,
LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14,
LEJP_REJECT_MP_C_OR_E_UNDERF = -15,
LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16,
LEJP_REJECT_MP_ARRAY_END_MISSING = -17,
LEJP_REJECT_STACK_OVERFLOW = -18,
LEJP_REJECT_MP_DELIM_ISTACK = -19,
LEJP_REJECT_NUM_TOO_LONG = -20,
LEJP_REJECT_MP_C_OR_E_NEITHER = -21,
LEJP_REJECT_UNKNOWN = -22,
LEJP_REJECT_CALLBACK = -23
};
#define LEJP_FLAG_CB_IS_VALUE 64
enum lejp_callbacks {
LEJPCB_CONSTRUCTED = 0,
LEJPCB_DESTRUCTED = 1,
LEJPCB_START = 2,
LEJPCB_COMPLETE = 3,
LEJPCB_FAILED = 4,
LEJPCB_PAIR_NAME = 5,
LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6,
LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7,
LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8,
LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9,
LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10,
LEJPCB_VAL_STR_START = 11, /* notice handle separately */
LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12,
LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13,
LEJPCB_ARRAY_START = 14,
LEJPCB_ARRAY_END = 15,
LEJPCB_OBJECT_START = 16,
LEJPCB_OBJECT_END = 17
};
/**
* _lejp_callback() - User parser actions
* \param ctx: LEJP context
* \param reason: Callback reason
*
* Your user callback is associated with the context at construction time,
* and receives calls as the parsing progresses.
*
* All of the callbacks may be ignored and just return 0.
*
* The reasons it might get called, found in @reason, are:
*
* LEJPCB_CONSTRUCTED: The context was just constructed... you might want to
* perform one-time allocation for the life of the context.
*
* LEJPCB_DESTRUCTED: The context is being destructed... if you made any
* allocations at construction-time, you can free them now
*
* LEJPCB_START: Parsing is beginning at the first byte of input
*
* LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or
* positive return code from lejp_parse indicating the
* amount of unused bytes left in the input buffer
*
* LEJPCB_FAILED: Parsing failed. You'll get a negative error code
* returned from lejp_parse
*
* LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed,
* this callback occurs. You can find the new name at
* the end of ctx->path[]
*
* LEJPCB_VAL_TRUE: The "true" value appeared
*
* LEJPCB_VAL_FALSE: The "false" value appeared
*
* LEJPCB_VAL_NULL: The "null" value appeared
*
* LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf
*
* LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf
*
* LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet
*
* LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in
* ctx->buf, which is as much as we can buffer, so we are
* spilling it. If all your strings are less than
* LEJP_STRING_CHUNK - 1 bytes, you will never see this
* callback.
*
* LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the
* string is in ctx->buf.
*
* LEJPCB_ARRAY_START: An array started
*
* LEJPCB_ARRAY_END: An array ended
*
* LEJPCB_OBJECT_START: An object started
*
* LEJPCB_OBJECT_END: An object ended
*/
LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason);
typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
#ifndef LEJP_MAX_DEPTH
#define LEJP_MAX_DEPTH 12
#endif
#ifndef LEJP_MAX_INDEX_DEPTH
#define LEJP_MAX_INDEX_DEPTH 5
#endif
#ifndef LEJP_MAX_PATH
#define LEJP_MAX_PATH 128
#endif
#ifndef LEJP_STRING_CHUNK
/* must be >= 30 to assemble floats */
#define LEJP_STRING_CHUNK 255
#endif
enum num_flags {
LEJP_SEEN_MINUS = (1 << 0),
LEJP_SEEN_POINT = (1 << 1),
LEJP_SEEN_POST_POINT = (1 << 2),
LEJP_SEEN_EXP = (1 << 3)
};
struct _lejp_stack {
char s; /* lejp_state stack*/
char p; /* path length */
char i; /* index array length */
char b; /* user bitfield */
};
struct lejp_ctx {
/* sorted by type for most compact alignment
*
* pointers
*/
signed char (*callback)(struct lejp_ctx *ctx, char reason);
void *user;
const char * const *paths;
/* arrays */
struct _lejp_stack st[LEJP_MAX_DEPTH];
unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */
unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
char path[LEJP_MAX_PATH];
char buf[LEJP_STRING_CHUNK];
/* int */
unsigned int line;
/* short */
unsigned short uni;
/* char */
unsigned char npos;
unsigned char dcount;
unsigned char f;
unsigned char sp; /* stack head */
unsigned char ipos; /* index stack depth */
unsigned char ppos;
unsigned char count_paths;
unsigned char path_match;
unsigned char path_match_len;
unsigned char wildcount;
};
LWS_VISIBLE LWS_EXTERN void
lejp_construct(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason),
void *user, const char * const *paths, unsigned char paths_count);
LWS_VISIBLE LWS_EXTERN void
lejp_destruct(struct lejp_ctx *ctx);
LWS_VISIBLE LWS_EXTERN int
lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
LWS_VISIBLE LWS_EXTERN void
lejp_change_callback(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason));
LWS_VISIBLE LWS_EXTERN int
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);

300
thirdparty/lws/misc/sha-1.c vendored Normal file
View File

@ -0,0 +1,300 @@
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* FIPS pub 180-1: Secure Hash Algorithm (SHA-1)
* based on: http://csrc.nist.gov/fips/fip180-1.txt
* implemented by Jun-ichiro itojun Itoh <itojun@itojun.org>
*/
#include "private-libwebsockets.h"
#ifdef LWS_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
struct sha1_ctxt {
union {
unsigned char b8[20];
unsigned int b32[5];
} h;
union {
unsigned char b8[8];
u_int64_t b64[1];
} c;
union {
unsigned char b8[64];
unsigned int b32[16];
} m;
unsigned char count;
};
/* sanity check */
#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
# define unsupported 1
#elif BYTE_ORDER != BIG_ENDIAN
# if BYTE_ORDER != LITTLE_ENDIAN
# define unsupported 1
# endif
#endif
#ifndef unsupported
/* constant table */
static const unsigned int _K[] =
{ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 };
#define K(t) _K[(t) / 20]
#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d)))
#define F1(b, c, d) (((b) ^ (c)) ^ (d))
#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
#define F3(b, c, d) (((b) ^ (c)) ^ (d))
#define S(n, x) (((x) << (n)) | ((x) >> (32 - n)))
#define H(n) (ctxt->h.b32[(n)])
#define COUNT (ctxt->count)
#define BCOUNT (ctxt->c.b64[0] / 8)
#define W(n) (ctxt->m.b32[(n)])
#define PUTBYTE(x) { \
ctxt->m.b8[(COUNT % 64)] = (x); \
COUNT++; \
COUNT %= 64; \
ctxt->c.b64[0] += 8; \
if (COUNT % 64 == 0) \
sha1_step(ctxt); \
}
#define PUTPAD(x) { \
ctxt->m.b8[(COUNT % 64)] = (x); \
COUNT++; \
COUNT %= 64; \
if (COUNT % 64 == 0) \
sha1_step(ctxt); \
}
static void
sha1_step(struct sha1_ctxt *ctxt)
{
unsigned int a, b, c, d, e, tmp;
size_t t, s;
#if BYTE_ORDER == LITTLE_ENDIAN
struct sha1_ctxt tctxt;
memcpy(&tctxt.m.b8[0], &ctxt->m.b8[0], 64);
ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2];
ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0];
ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6];
ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4];
ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10];
ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8];
ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14];
ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12];
ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18];
ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16];
ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22];
ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20];
ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26];
ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24];
ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30];
ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28];
ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34];
ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32];
ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38];
ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36];
ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42];
ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40];
ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46];
ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44];
ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50];
ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48];
ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54];
ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52];
ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58];
ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56];
ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62];
ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60];
#endif
a = H(0); b = H(1); c = H(2); d = H(3); e = H(4);
for (t = 0; t < 20; t++) {
s = t & 0x0f;
if (t >= 16)
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 20; t < 40; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 40; t < 60; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 60; t < 80; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
H(0) = H(0) + a;
H(1) = H(1) + b;
H(2) = H(2) + c;
H(3) = H(3) + d;
H(4) = H(4) + e;
bzero(&ctxt->m.b8[0], 64);
}
/*------------------------------------------------------------*/
static void
_sha1_init(struct sha1_ctxt *ctxt)
{
bzero(ctxt, sizeof(struct sha1_ctxt));
H(0) = 0x67452301;
H(1) = 0xefcdab89;
H(2) = 0x98badcfe;
H(3) = 0x10325476;
H(4) = 0xc3d2e1f0;
}
void
sha1_pad(struct sha1_ctxt *ctxt)
{
size_t padlen; /*pad length in bytes*/
size_t padstart;
PUTPAD(0x80);
padstart = COUNT % 64;
padlen = 64 - padstart;
if (padlen < 8) {
bzero(&ctxt->m.b8[padstart], padlen);
COUNT += (unsigned char)padlen;
COUNT %= 64;
sha1_step(ctxt);
padstart = COUNT % 64; /* should be 0 */
padlen = 64 - padstart; /* should be 64 */
}
bzero(&ctxt->m.b8[padstart], padlen - 8);
COUNT += ((unsigned char)padlen - 8);
COUNT %= 64;
#if BYTE_ORDER == BIG_ENDIAN
PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]);
PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]);
PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]);
PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]);
#else
PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]);
PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]);
PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]);
PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]);
#endif
}
void
sha1_loop(struct sha1_ctxt *ctxt, const unsigned char *input, size_t len)
{
size_t gaplen;
size_t gapstart;
size_t off;
size_t copysiz;
off = 0;
while (off < len) {
gapstart = COUNT % 64;
gaplen = 64 - gapstart;
copysiz = (gaplen < len - off) ? gaplen : len - off;
memcpy(&ctxt->m.b8[gapstart], &input[off], copysiz);
COUNT += (unsigned char)copysiz;
COUNT %= 64;
ctxt->c.b64[0] += copysiz * 8;
if (COUNT % 64 == 0)
sha1_step(ctxt);
off += copysiz;
}
}
void
sha1_result(struct sha1_ctxt *ctxt, void *digest0)
{
unsigned char *digest;
digest = (unsigned char *)digest0;
sha1_pad(ctxt);
#if BYTE_ORDER == BIG_ENDIAN
memcpy(digest, &ctxt->h.b8[0], 20);
#else
digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2];
digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0];
digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6];
digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4];
digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10];
digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8];
digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14];
digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12];
digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18];
digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16];
#endif
}
/*
* This should look and work like the libcrypto implementation
*/
LWS_VISIBLE unsigned char *
lws_SHA1(const unsigned char *d, size_t n, unsigned char *md)
{
struct sha1_ctxt ctx;
_sha1_init(&ctx);
sha1_loop(&ctx, d, n);
sha1_result(&ctx, (void *)md);
return md;
}
#endif /*unsupported*/

877
thirdparty/lws/output.c vendored Normal file
View File

@ -0,0 +1,877 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
static int
lws_0405_frame_mask_generate(struct lws *wsi)
{
#if 0
wsi->u.ws.mask[0] = 0;
wsi->u.ws.mask[1] = 0;
wsi->u.ws.mask[2] = 0;
wsi->u.ws.mask[3] = 0;
#else
int n;
/* fetch the per-frame nonce */
n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask, 4);
if (n != 4) {
lwsl_parser("Unable to read from random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, n);
return 1;
}
#endif
/* start masking from first byte of masking key buffer */
wsi->u.ws.mask_idx = 0;
return 0;
}
/*
* notice this returns number of bytes consumed, or -1
*/
int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
size_t real_len = len;
unsigned int n;
int m;
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);
if (!len)
return 0;
/* just ignore sends after we cleared the truncation buffer */
if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
!wsi->trunc_len)
return len;
if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
char dump[20];
strncpy(dump, (char *)buf, sizeof(dump) - 1);
dump[sizeof(dump) - 1] = '\0';
#if defined(LWS_WITH_ESP8266)
lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n",
wsi, (unsigned long)len, dump);
#else
lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n"
" It's illegal to do an lws_write outside of\n"
" the writable callback: fix your code\n",
wsi, (unsigned long)len, dump);
#endif
assert(0);
return -1;
}
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, len);
if (m < 0)
return -1;
if (m) /* handled */ {
n = m;
goto handle_truncated_send;
}
if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
lwsl_warn("** error invalid sock but expected to send\n");
/* limit sending */
if (wsi->protocol->tx_packet_size)
n = wsi->protocol->tx_packet_size;
else {
n = wsi->protocol->rx_buffer_size;
if (!n)
n = context->pt_serv_buf_size;
}
n += LWS_PRE + 4;
if (n > len)
n = len;
#if defined(LWS_WITH_ESP8266)
if (wsi->pending_send_completion) {
n = 0;
goto handle_truncated_send;
}
#endif
/* nope, send it on the socket directly */
lws_latency_pre(context, wsi);
n = lws_ssl_capable_write(wsi, buf, n);
lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
/* we're going to close, let close know sends aren't possible */
wsi->socket_is_permanently_unusable = 1;
return -1;
case LWS_SSL_CAPABLE_MORE_SERVICE:
/* nothing got sent, not fatal, retry the whole thing later */
n = 0;
break;
}
handle_truncated_send:
/*
* we were already handling a truncated send?
*/
if (wsi->trunc_len) {
lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
wsi->trunc_offset += n;
wsi->trunc_len -= n;
if (!wsi->trunc_len) {
lwsl_info("***** %p partial send completed\n", wsi);
/* done with it, but don't free it */
n = real_len;
if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
lwsl_info("***** %p signalling to close now\n", wsi);
return -1; /* retry closing now */
}
}
/* always callback on writeable */
lws_callback_on_writable(wsi);
return n;
}
if ((unsigned int)n == real_len)
/* what we just sent went out cleanly */
return n;
/*
* Newly truncated send. Buffer the remainder (it will get
* first priority next time the socket is writable)
*/
lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
(unsigned long)real_len);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);
/*
* - if we still have a suitable malloc lying around, use it
* - or, if too small, reallocate it
* - or, if no buffer, create it
*/
if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
lws_free(wsi->trunc_alloc);
wsi->trunc_alloc_len = real_len - n;
wsi->trunc_alloc = lws_malloc(real_len - n, "truncated send alloc");
if (!wsi->trunc_alloc) {
lwsl_err("truncated send: unable to malloc %lu\n",
(unsigned long)(real_len - n));
return -1;
}
}
wsi->trunc_offset = 0;
wsi->trunc_len = real_len - n;
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
/* since something buffered, force it to get another chance to send */
lws_callback_on_writable(wsi);
return real_len;
}
LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol wp)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int masked7 = (wsi->mode == LWSCM_WS_CLIENT);
unsigned char is_masked_bit = 0;
unsigned char *dropmask = NULL;
struct lws_tokens eff_buf;
size_t orig_len = len;
int pre = 0, n;
if (wsi->parent_carries_io) {
struct lws_write_passthru pas;
pas.buf = buf;
pas.len = len;
pas.wp = wp;
pas.wsi = wsi;
if (wsi->parent->protocol->callback(wsi->parent,
LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
wsi->parent->user_space,
(void *)&pas, 0))
return 1;
return len;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
if ((int)len < 0) {
lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__,
(int)len, (unsigned long)len);
return -1;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len);
#ifdef LWS_WITH_ACCESS_LOG
wsi->access_log.sent += len;
#endif
if (wsi->vhost)
wsi->vhost->conn_stats.tx += len;
if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
/* remove us from the list */
struct lws **w = &pt->tx_draining_ext_list;
wsi->u.ws.tx_draining_ext = 0;
/* remove us from context draining ext list */
while (*w) {
if (*w == wsi) {
*w = wsi->u.ws.tx_draining_ext_list;
break;
}
w = &((*w)->u.ws.tx_draining_ext_list);
}
wsi->u.ws.tx_draining_ext_list = NULL;
wp = (wsi->u.ws.tx_draining_stashed_wp & 0xc0) |
LWS_WRITE_CONTINUATION;
lwsl_ext("FORCED draining wp to 0x%02X\n", wp);
}
lws_restart_ws_ping_pong_timer(wsi);
if ((wp & 0x1f) == LWS_WRITE_HTTP ||
(wp & 0x1f) == LWS_WRITE_HTTP_FINAL ||
(wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
(wp & 0x1f) == LWS_WRITE_HTTP_HEADERS)
goto send_raw;
/* if not in a state to send stuff, then just send nothing */
if (wsi->state != LWSS_ESTABLISHED &&
((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
wp != LWS_WRITE_CLOSE))
return 0;
/* if we are continuing a frame that already had its header done */
if (wsi->u.ws.inside_frame) {
lwsl_debug("INSIDE FRAME\n");
goto do_more_inside_frame;
}
wsi->u.ws.clean_buffer = 1;
/*
* give a chance to the extensions to modify payload
* the extension may decide to produce unlimited payload erratically
* (eg, compression extension), so we require only that if he produces
* something, it will be a complete fragment of the length known at
* the time (just the fragment length known), and if he has
* more we will come back next time he is writeable and allow him to
* produce more fragments until he's drained.
*
* This allows what is sent each time it is writeable to be limited to
* a size that can be sent without partial sends or blocking, allows
* interleaving of control frames and other connection service.
*/
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
switch ((int)wp) {
case LWS_WRITE_PING:
case LWS_WRITE_PONG:
case LWS_WRITE_CLOSE:
break;
default:
lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n");
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp);
if (n < 0)
return -1;
if (n && eff_buf.token_len) {
lwsl_debug("drain len %d\n", (int)eff_buf.token_len);
/* extension requires further draining */
wsi->u.ws.tx_draining_ext = 1;
wsi->u.ws.tx_draining_ext_list = pt->tx_draining_ext_list;
pt->tx_draining_ext_list = wsi;
/* we must come back to do more */
lws_callback_on_writable(wsi);
/*
* keep a copy of the write type for the overall
* action that has provoked generation of these
* fragments, so the last guy can use its FIN state.
*/
wsi->u.ws.tx_draining_stashed_wp = wp;
/* this is definitely not actually the last fragment
* because the extension asserted he has more coming
* So make sure this intermediate one doesn't go out
* with a FIN.
*/
wp |= LWS_WRITE_NO_FIN;
}
if (eff_buf.token_len && wsi->u.ws.stashed_write_pending) {
wsi->u.ws.stashed_write_pending = 0;
wp = (wp &0xc0) | (int)wsi->u.ws.stashed_write_type;
}
}
/*
* an extension did something we need to keep... for example, if
* compression extension, it has already updated its state according
* to this being issued
*/
if ((char *)buf != eff_buf.token) {
/*
* ext might eat it, but not have anything to issue yet.
* In that case we have to follow his lead, but stash and
* replace the write type that was lost here the first time.
*/
if (len && !eff_buf.token_len) {
if (!wsi->u.ws.stashed_write_pending)
wsi->u.ws.stashed_write_type = (char)wp & 0x3f;
wsi->u.ws.stashed_write_pending = 1;
return len;
}
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->u.ws.clean_buffer = 0;
}
buf = (unsigned char *)eff_buf.token;
len = eff_buf.token_len;
if (!buf) {
lwsl_err("null buf (%d)\n", (int)len);
return -1;
}
switch (wsi->ietf_spec_revision) {
case 13:
if (masked7) {
pre += 4;
dropmask = &buf[0 - pre];
is_masked_bit = 0x80;
}
switch (wp & 0xf) {
case LWS_WRITE_TEXT:
n = LWSWSOPC_TEXT_FRAME;
break;
case LWS_WRITE_BINARY:
n = LWSWSOPC_BINARY_FRAME;
break;
case LWS_WRITE_CONTINUATION:
n = LWSWSOPC_CONTINUATION;
break;
case LWS_WRITE_CLOSE:
n = LWSWSOPC_CLOSE;
break;
case LWS_WRITE_PING:
n = LWSWSOPC_PING;
break;
case LWS_WRITE_PONG:
n = LWSWSOPC_PONG;
break;
default:
lwsl_warn("lws_write: unknown write opc / wp\n");
return -1;
}
if (!(wp & LWS_WRITE_NO_FIN))
n |= 1 << 7;
if (len < 126) {
pre += 2;
buf[-pre] = n;
buf[-pre + 1] = (unsigned char)(len | is_masked_bit);
} else {
if (len < 65536) {
pre += 4;
buf[-pre] = n;
buf[-pre + 1] = 126 | is_masked_bit;
buf[-pre + 2] = (unsigned char)(len >> 8);
buf[-pre + 3] = (unsigned char)len;
} else {
pre += 10;
buf[-pre] = n;
buf[-pre + 1] = 127 | is_masked_bit;
#if defined __LP64__
buf[-pre + 2] = (len >> 56) & 0x7f;
buf[-pre + 3] = len >> 48;
buf[-pre + 4] = len >> 40;
buf[-pre + 5] = len >> 32;
#else
buf[-pre + 2] = 0;
buf[-pre + 3] = 0;
buf[-pre + 4] = 0;
buf[-pre + 5] = 0;
#endif
buf[-pre + 6] = (unsigned char)(len >> 24);
buf[-pre + 7] = (unsigned char)(len >> 16);
buf[-pre + 8] = (unsigned char)(len >> 8);
buf[-pre + 9] = (unsigned char)len;
}
}
break;
}
do_more_inside_frame:
/*
* Deal with masking if we are in client -> server direction and
* the wp demands it
*/
if (masked7) {
if (!wsi->u.ws.inside_frame)
if (lws_0405_frame_mask_generate(wsi)) {
lwsl_err("frame mask generation failed\n");
return -1;
}
/*
* in v7, just mask the payload
*/
if (dropmask) { /* never set if already inside frame */
for (n = 4; n < (int)len + 4; n++)
dropmask[n] = dropmask[n] ^ wsi->u.ws.mask[
(wsi->u.ws.mask_idx++) & 3];
/* copy the frame nonce into place */
memcpy(dropmask, wsi->u.ws.mask, 4);
}
}
send_raw:
switch ((int)(wp & 0x1f)) {
case LWS_WRITE_CLOSE:
/* lwsl_hexdump(&buf[-pre], len); */
case LWS_WRITE_HTTP:
case LWS_WRITE_HTTP_FINAL:
case LWS_WRITE_HTTP_HEADERS:
case LWS_WRITE_HTTP_HEADERS_CONTINUATION:
case LWS_WRITE_PONG:
case LWS_WRITE_PING:
#ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING) {
unsigned char flags = 0;
n = LWS_H2_FRAME_TYPE_DATA;
if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) {
n = LWS_H2_FRAME_TYPE_HEADERS;
if (!(wp & LWS_WRITE_NO_FIN))
flags = LWS_H2_FLAG_END_HEADERS;
if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
flags |= LWS_H2_FLAG_END_STREAM;
wsi->u.h2.send_END_STREAM = 1;
}
}
if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
n = LWS_H2_FRAME_TYPE_CONTINUATION;
if (!(wp & LWS_WRITE_NO_FIN))
flags = LWS_H2_FLAG_END_HEADERS;
if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
flags |= LWS_H2_FLAG_END_STREAM;
wsi->u.h2.send_END_STREAM = 1;
}
}
if (((wp & 0x1f) == LWS_WRITE_HTTP ||
(wp & 0x1f) == LWS_WRITE_HTTP_FINAL) &&
wsi->u.http.tx_content_length) {
wsi->u.http.tx_content_remain -= len;
lwsl_info("%s: content_remain = %llu\n", __func__,
(unsigned long long)wsi->u.http.tx_content_remain);
if (!wsi->u.http.tx_content_remain) {
lwsl_info("%s: selecting final write mode\n", __func__);
wp = LWS_WRITE_HTTP_FINAL;
}
}
if ((wp & 0x1f) == LWS_WRITE_HTTP_FINAL || (wp & LWS_WRITE_H2_STREAM_END)) {
//lws_get_network_wsi(wsi)->u.h2.END_STREAM) {
lwsl_info("%s: setting END_STREAM\n", __func__);
flags |= LWS_H2_FLAG_END_STREAM;
wsi->u.h2.send_END_STREAM = 1;
}
return lws_h2_frame_write(wsi, n, flags,
wsi->u.h2.my_sid, len, buf);
}
#endif
return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
default:
break;
}
/*
* give any active extensions a chance to munge the buffer
* before send. We pass in a pointer to an lws_tokens struct
* prepared with the default buffer and content length that's in
* there. Rather than rewrite the default buffer, extensions
* that expect to grow the buffer can adapt .token to
* point to their own per-connection buffer in the extension
* user allocation. By default with no extensions or no
* extension callback handling, just the normal input buffer is
* used then so it is efficient.
*
* callback returns 1 in case it wants to spill more buffers
*
* This takes care of holding the buffer if send is incomplete, ie,
* if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with
* the buffer). If wsi->u.ws.clean_buffer is 1, it will instead
* return to the user code how much OF THE USER BUFFER was consumed.
*/
n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
wsi->u.ws.inside_frame = 1;
if (n <= 0)
return n;
if (n == (int)len + pre) {
/* everything in the buffer was handled (or rebuffered...) */
wsi->u.ws.inside_frame = 0;
return orig_len;
}
/*
* it is how many bytes of user buffer got sent... may be < orig_len
* in which case callback when writable has already been arranged
* and user code can call lws_write() again with the rest
* later.
*/
return n - pre;
}
LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_process_html_args args;
lws_filepos_t amount, poss;
unsigned char *p, *pstart;
#if defined(LWS_WITH_RANGES)
unsigned char finished = 0;
#endif
int n, m;
lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
while (!lws_send_pipe_choked(wsi)) {
if (wsi->trunc_len) {
if (lws_issue_raw(wsi, wsi->trunc_alloc +
wsi->trunc_offset,
wsi->trunc_len) < 0) {
lwsl_info("%s: closing\n", __func__);
goto file_had_it;
}
continue;
}
if (wsi->u.http.filepos == wsi->u.http.filelen)
goto all_sent;
n = 0;
pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
p = pstart;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.count_ranges && !wsi->u.http.range.inside) {
lwsl_notice("%s: doing range start %llu\n", __func__, wsi->u.http.range.start);
if ((long long)lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
wsi->u.http.range.start -
wsi->u.http.filepos) < 0)
goto file_had_it;
wsi->u.http.filepos = wsi->u.http.range.start;
if (wsi->u.http.range.count_ranges > 1) {
n = lws_snprintf((char *)p, context->pt_serv_buf_size - LWS_H2_FRAME_HEADER_LENGTH,
"_lws\x0d\x0a"
"Content-Type: %s\x0d\x0a"
"Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
"\x0d\x0a",
wsi->u.http.multipart_content_type,
wsi->u.http.range.start,
wsi->u.http.range.end,
wsi->u.http.range.extent);
p += n;
}
wsi->u.http.range.budget = wsi->u.http.range.end -
wsi->u.http.range.start + 1;
wsi->u.http.range.inside = 1;
}
#endif
poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
/*
* if there is a hint about how much we will do well to send at one time,
* restrict ourselves to only trying to send that.
*/
if (wsi->protocol->tx_packet_size &&
poss > wsi->protocol->tx_packet_size)
poss = wsi->protocol->tx_packet_size;
#if defined(LWS_WITH_HTTP2)
m = lws_h2_tx_cr_get(wsi);
if (!m) {
lwsl_info("%s: came here with no tx credit", __func__);
return 0;
}
if (m < poss)
poss = m;
/*
* consumption of the actual payload amount sent will be handled
* when the http2 data frame is sent
*/
#endif
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.count_ranges) {
if (wsi->u.http.range.count_ranges > 1)
poss -= 7; /* allow for final boundary */
if (poss > wsi->u.http.range.budget)
poss = wsi->u.http.range.budget;
}
#endif
if (wsi->sending_chunked) {
/* we need to drop the chunk size in here */
p += 10;
/* allow for the chunk to grow by 128 in translation */
poss -= 10 + 128;
}
if (lws_vfs_file_read(wsi->u.http.fop_fd, &amount, p, poss) < 0)
goto file_had_it; /* caller will close */
if (wsi->sending_chunked)
n = (int)amount;
else
n = (p - pstart) + (int)amount;
lwsl_debug("%s: sending %d\n", __func__, n);
if (n) {
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
context->timeout_secs);
if (wsi->sending_chunked) {
args.p = (char *)p;
args.len = n;
args.max_len = (unsigned int)poss + 128;
args.final = wsi->u.http.filepos + n ==
wsi->u.http.filelen;
if (user_callback_handle_rxflow(
wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi,
LWS_CALLBACK_PROCESS_HTML,
wsi->user_space, &args, 0) < 0)
goto file_had_it;
n = args.len;
p = (unsigned char *)args.p;
} else
p = pstart;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.send_ctr + 1 ==
wsi->u.http.range.count_ranges && // last range
wsi->u.http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
wsi->u.http.range.budget - amount == 0) {// final part
n += lws_snprintf((char *)pstart + n, 6,
"_lws\x0d\x0a"); // append trailing boundary
lwsl_debug("added trailing boundary\n");
}
#endif
m = lws_write(wsi, p, n,
wsi->u.http.filepos == wsi->u.http.filelen ?
LWS_WRITE_HTTP_FINAL :
LWS_WRITE_HTTP
);
if (m < 0)
goto file_had_it;
wsi->u.http.filepos += amount;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.count_ranges >= 1) {
wsi->u.http.range.budget -= amount;
if (wsi->u.http.range.budget == 0) {
lwsl_notice("range budget exhausted\n");
wsi->u.http.range.inside = 0;
wsi->u.http.range.send_ctr++;
if (lws_ranges_next(&wsi->u.http.range) < 1) {
finished = 1;
goto all_sent;
}
}
}
#endif
if (m != n) {
/* adjust for what was not sent */
if (lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
m - n) ==
(unsigned long)-1)
goto file_had_it;
}
}
all_sent:
if ((!wsi->trunc_len && wsi->u.http.filepos >= wsi->u.http.filelen)
#if defined(LWS_WITH_RANGES)
|| finished)
#else
)
#endif
{
wsi->state = LWSS_HTTP;
/* we might be in keepalive, so close it off here */
lws_vfs_file_close(&wsi->u.http.fop_fd);
lwsl_debug("file completed\n");
if (wsi->protocol->callback &&
user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
wsi->user_space, NULL,
0) < 0) {
/*
* For http/1.x, the choices from
* transaction_completed are either
* 0 to use the connection for pipelined
* or nonzero to hang it up.
*
* However for http/2. while we are
* still interested in hanging up the
* nwsi if there was a network-level
* fatal error, simply completing the
* transaction is a matter of the stream
* state, not the root connection at the
* network level
*/
if (wsi->http2_substream)
return 1;
else
return -1;
}
return 1; /* >0 indicates completed */
}
}
lws_callback_on_writable(wsi);
return 0; /* indicates further processing must be done */
file_had_it:
lws_vfs_file_close(&wsi->u.http.fop_fd);
return -1;
}
#if LWS_POSIX
LWS_VISIBLE int
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n;
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
if (n >= 0) {
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
lws_restart_ws_ping_pong_timer(wsi);
return n;
}
#if LWS_POSIX
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR)
return LWS_SSL_CAPABLE_MORE_SERVICE;
#endif
lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
LWS_VISIBLE int
lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
int n = 0;
#if LWS_POSIX
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
// lwsl_info("%s: sent len %d result %d", __func__, len, n);
if (n >= 0)
return n;
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR) {
if (LWS_ERRNO == LWS_EWOULDBLOCK) {
lws_set_blocking_send(wsi);
}
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
#else
(void)n;
(void)wsi;
(void)buf;
(void)len;
// !!!
#endif
lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n",
len, wsi->desc.sockfd, n, LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
#endif
LWS_VISIBLE int
lws_ssl_pending_no_ssl(struct lws *wsi)
{
(void)wsi;
#if defined(LWS_WITH_ESP32)
return 100;
#else
return 0;
#endif
}

850
thirdparty/lws/plat/lws-plat-unix.c vendored Normal file
View File

@ -0,0 +1,850 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include <pwd.h>
#include <grp.h>
#ifdef LWS_WITH_PLUGINS
#include <dlfcn.h>
#endif
#include <dirent.h>
unsigned long long time_in_microseconds(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec;
}
LWS_VISIBLE int
lws_get_random(struct lws_context *context, void *buf, int len)
{
return read(context->fd_random, (char *)buf, len);
}
LWS_VISIBLE int
lws_send_pipe_choked(struct lws *wsi)
{
struct lws_pollfd fds;
struct lws *wsi_eff = wsi;
#if defined(LWS_WITH_HTTP2)
wsi_eff = lws_get_network_wsi(wsi);
#endif
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi_eff->trunc_len)
return 1;
fds.fd = wsi_eff->desc.sockfd;
fds.events = POLLOUT;
fds.revents = 0;
if (poll(&fds, 1, 0) != 1)
return 1;
if ((fds.revents & POLLOUT) == 0)
return 1;
/* okay to send another packet without blocking */
return 0;
}
LWS_VISIBLE int
lws_poll_listen_fd(struct lws_pollfd *fd)
{
return poll(fd, 1, 0);
}
LWS_VISIBLE void
lws_cancel_service_pt(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char buf = 0;
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
}
LWS_VISIBLE void
lws_cancel_service(struct lws_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
char buf = 0, m = context->count_threads;
while (m--) {
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
pt++;
}
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
int syslog_level = LOG_DEBUG;
switch (level) {
case LLL_ERR:
syslog_level = LOG_ERR;
break;
case LLL_WARN:
syslog_level = LOG_WARNING;
break;
case LLL_NOTICE:
syslog_level = LOG_NOTICE;
break;
case LLL_INFO:
syslog_level = LOG_INFO;
break;
}
syslog(syslog_level, "%s", line);
}
LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{
struct lws_context_per_thread *pt;
int n = -1, m, c;
char buf;
/* stay dead once we are dead */
if (!context || !context->vhost_list)
return 1;
pt = &context->pt[tsi];
lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1);
if (timeout_ms < 0)
goto faked_service;
lws_libev_run(context, tsi);
lws_libuv_run(context, tsi);
lws_libevent_run(context, tsi);
if (!context->service_tid_detected) {
struct lws _lws;
memset(&_lws, 0, sizeof(_lws));
_lws.context = context;
context->service_tid_detected =
context->vhost_list->protocols[0].callback(
&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
context->service_tid = context->service_tid_detected;
context->service_tid_detected = 1;
}
/*
* is there anybody with pending stuff that needs service forcing?
*/
if (!lws_service_adjust_timeout(context, 1, tsi)) {
/* -1 timeout means just do forced service */
_lws_plat_service_tsi(context, -1, pt->tid);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(context, 1, pt->tid))
/* yes... come back again quickly */
timeout_ms = 0;
}
n = poll(pt->fds, pt->fds_count, timeout_ms);
#ifdef LWS_OPENSSL_SUPPORT
if (!n && !pt->rx_draining_ext_list &&
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {
#else
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
#endif
lws_service_fd_tsi(context, NULL, tsi);
return 0;
}
faked_service:
m = lws_service_flag_pending(context, tsi);
if (m)
c = -1; /* unknown limit */
else
if (n < 0) {
if (LWS_ERRNO != LWS_EINTR)
return -1;
return 0;
} else
c = n;
/* any socket with events to service? */
for (n = 0; n < pt->fds_count && c; n++) {
if (!pt->fds[n].revents)
continue;
c--;
if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) {
if (read(pt->fds[n].fd, &buf, 1) != 1)
lwsl_err("Cannot read from dummy pipe.");
continue;
}
m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
if (m < 0)
return -1;
/* if something closed, retry this slot */
if (m)
n--;
}
return 0;
}
LWS_VISIBLE int
lws_plat_check_connection_error(struct lws *wsi)
{
return 0;
}
LWS_VISIBLE int
lws_plat_service(struct lws_context *context, int timeout_ms)
{
return _lws_plat_service_tsi(context, timeout_ms, 0);
}
LWS_VISIBLE int
lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
{
int optval = 1;
socklen_t optlen = sizeof(optval);
#if defined(__APPLE__) || \
defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || \
defined(__OpenBSD__) || \
defined(__HAIKU__)
struct protoent *tcp_proto;
#endif
if (vhost->ka_time) {
/* enable keepalive on this socket */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
(const void *)&optval, optlen) < 0)
return 1;
#if defined(__APPLE__) || \
defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || \
defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) || \
defined(__HAIKU__)
/*
* didn't find a way to set these per-socket, need to
* tune kernel systemwide values
*/
#else
/* set the keepalive conditions we want on it too */
optval = vhost->ka_time;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
(const void *)&optval, optlen) < 0)
return 1;
optval = vhost->ka_interval;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
(const void *)&optval, optlen) < 0)
return 1;
optval = vhost->ka_probes;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT,
(const void *)&optval, optlen) < 0)
return 1;
#endif
}
#if defined(SO_BINDTODEVICE)
if (vhost->bind_iface && vhost->iface) {
lwsl_info("binding listen skt to %s using SO_BINDTODEVICE\n", vhost->iface);
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, vhost->iface,
strlen(vhost->iface)) < 0) {
lwsl_warn("Failed to bind to device %s\n", vhost->iface);
return 1;
}
}
#endif
/* Disable Nagle */
optval = 1;
#if defined (__sun)
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
return 1;
#elif !defined(__APPLE__) && \
!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \
!defined(__NetBSD__) && \
!defined(__OpenBSD__) && \
!defined(__HAIKU__)
if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
return 1;
#else
tcp_proto = getprotobyname("TCP");
if (setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0)
return 1;
#endif
/* We are nonblocking... */
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
return 1;
return 0;
}
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
static void
_lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
{
cap_t caps;
if (!count)
return;
caps = cap_get_proc();
cap_set_flag(caps, mode, count, cv, CAP_SET);
cap_set_proc(caps);
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
cap_free(caps);
}
#endif
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
int n;
#endif
if (info->gid && info->gid != -1)
if (setgid(info->gid))
lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
if (info->uid && info->uid != -1) {
struct passwd *p = getpwuid(info->uid);
if (p) {
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
_lws_plat_apply_caps(CAP_PERMITTED, info->caps, info->count_caps);
#endif
initgroups(p->pw_name, info->gid);
if (setuid(info->uid))
lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO));
else
lwsl_notice("Set privs to user '%s'\n", p->pw_name);
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
_lws_plat_apply_caps(CAP_EFFECTIVE, info->caps, info->count_caps);
if (info->count_caps)
for (n = 0; n < info->count_caps; n++)
lwsl_notice(" RETAINING CAPABILITY %d\n", (int)info->caps[n]);
#endif
} else
lwsl_warn("getpwuid: unable to find uid %d", info->uid);
}
}
#ifdef LWS_WITH_PLUGINS
#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0
/* libuv.c implements these in a cross-platform way */
#else
static int filter(const struct dirent *ent)
{
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
return 0;
return 1;
}
LWS_VISIBLE int
lws_plat_plugins_init(struct lws_context * context, const char * const *d)
{
struct lws_plugin_capability lcaps;
struct lws_plugin *plugin;
lws_plugin_init_func initfunc;
struct dirent **namelist;
int n, i, m, ret = 0;
char path[256];
void *l;
lwsl_notice(" Plugins:\n");
while (d && *d) {
n = scandir(*d, &namelist, filter, alphasort);
if (n < 0) {
lwsl_err("Scandir on %s failed\n", *d);
return 1;
}
for (i = 0; i < n; i++) {
if (strlen(namelist[i]->d_name) < 7)
goto inval;
lwsl_notice(" %s\n", namelist[i]->d_name);
lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d,
namelist[i]->d_name);
l = dlopen(path, RTLD_NOW);
if (!l) {
lwsl_err("Error loading DSO: %s\n", dlerror());
while (i++ < n)
free(namelist[i]);
goto bail;
}
/* we could open it, can we get his init function? */
m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
namelist[i]->d_name + 3 /* snip lib... */);
path[m - 3] = '\0'; /* snip the .so */
initfunc = dlsym(l, path);
if (!initfunc) {
lwsl_err("Failed to get init on %s: %s",
namelist[i]->d_name, dlerror());
dlclose(l);
}
lcaps.api_magic = LWS_PLUGIN_API_MAGIC;
m = initfunc(context, &lcaps);
if (m) {
lwsl_err("Initializing %s failed %d\n",
namelist[i]->d_name, m);
dlclose(l);
goto skip;
}
plugin = lws_malloc(sizeof(*plugin), "plugin");
if (!plugin) {
lwsl_err("OOM\n");
goto bail;
}
plugin->list = context->plugin_list;
context->plugin_list = plugin;
strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
plugin->name[sizeof(plugin->name) - 1] = '\0';
plugin->l = l;
plugin->caps = lcaps;
context->plugin_protocol_count += lcaps.count_protocols;
context->plugin_extension_count += lcaps.count_extensions;
free(namelist[i]);
continue;
skip:
dlclose(l);
inval:
free(namelist[i]);
}
free(namelist);
d++;
}
bail:
free(namelist);
return ret;
}
LWS_VISIBLE int
lws_plat_plugins_destroy(struct lws_context * context)
{
struct lws_plugin *plugin = context->plugin_list, *p;
lws_plugin_destroy_func func;
char path[256];
int m;
if (!plugin)
return 0;
lwsl_notice("%s\n", __func__);
while (plugin) {
p = plugin;
m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3);
path[m - 3] = '\0';
func = dlsym(plugin->l, path);
if (!func) {
lwsl_err("Failed to get destroy on %s: %s",
plugin->name, dlerror());
goto next;
}
m = func(context);
if (m)
lwsl_err("Initializing %s failed %d\n",
plugin->name, m);
next:
dlclose(p->l);
plugin = p->list;
p->list = NULL;
free(p);
}
context->plugin_list = NULL;
return 0;
}
#endif
#endif
#if 0
static void
sigabrt_handler(int x)
{
printf("%s\n", __func__);
}
#endif
LWS_VISIBLE int
lws_plat_context_early_init(void)
{
#if !defined(LWS_AVOID_SIGPIPE_IGN)
signal(SIGPIPE, SIG_IGN);
#endif
return 0;
}
LWS_VISIBLE void
lws_plat_context_early_destroy(struct lws_context *context)
{
}
LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
int m = context->count_threads;
#ifdef LWS_WITH_PLUGINS
if (context->plugin_list)
lws_plat_plugins_destroy(context);
#endif
if (context->lws_lookup)
lws_free(context->lws_lookup);
while (m--) {
if (pt->dummy_pipe_fds[0])
close(pt->dummy_pipe_fds[0]);
if (pt->dummy_pipe_fds[1])
close(pt->dummy_pipe_fds[1]);
pt++;
}
if (!context->fd_random)
lwsl_err("ZERO RANDOM FD\n");
if (context->fd_random != LWS_INVALID_FILE)
close(context->fd_random);
}
/* cast a struct sockaddr_in6 * into addr for ipv6 */
LWS_VISIBLE int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
size_t addrlen)
{
int rc = -1;
struct ifaddrs *ifr;
struct ifaddrs *ifc;
#ifdef LWS_WITH_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
#endif
getifaddrs(&ifr);
for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) {
if (!ifc->ifa_addr)
continue;
lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
if (strcmp(ifc->ifa_name, ifname))
continue;
switch (ifc->ifa_addr->sa_family) {
case AF_INET:
#ifdef LWS_WITH_IPV6
if (ipv6) {
/* map IPv4 to IPv6 */
bzero((char *)&addr6->sin6_addr,
sizeof(struct in6_addr));
addr6->sin6_addr.s6_addr[10] = 0xff;
addr6->sin6_addr.s6_addr[11] = 0xff;
memcpy(&addr6->sin6_addr.s6_addr[12],
&((struct sockaddr_in *)ifc->ifa_addr)->sin_addr,
sizeof(struct in_addr));
} else
#endif
memcpy(addr,
(struct sockaddr_in *)ifc->ifa_addr,
sizeof(struct sockaddr_in));
break;
#ifdef LWS_WITH_IPV6
case AF_INET6:
memcpy(&addr6->sin6_addr,
&((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr,
sizeof(struct in6_addr));
break;
#endif
default:
continue;
}
rc = 0;
}
freeifaddrs(ifr);
if (rc == -1) {
/* check if bind to IP address */
#ifdef LWS_WITH_IPV6
if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
rc = 0;
else
#endif
if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
rc = 0;
}
return rc;
}
LWS_VISIBLE void
lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
pt->fds[pt->fds_count++].revents = 0;
}
LWS_VISIBLE void
lws_plat_delete_socket_from_fds(struct lws_context *context,
struct lws *wsi, int m)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
pt->fds_count--;
}
LWS_VISIBLE void
lws_plat_service_periodic(struct lws_context *context)
{
/* if our parent went down, don't linger around */
if (context->started_with_parent &&
kill(context->started_with_parent, 0) < 0)
kill(getpid(), SIGTERM);
}
LWS_VISIBLE int
lws_plat_change_pollfd(struct lws_context *context,
struct lws *wsi, struct lws_pollfd *pfd)
{
return 0;
}
LWS_VISIBLE const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
{
return inet_ntop(af, src, dst, cnt);
}
LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst)
{
return inet_pton(af, src, dst);
}
LWS_VISIBLE lws_fop_fd_t
_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
const char *vpath, lws_fop_flags_t *flags)
{
struct stat stat_buf;
int ret = open(filename, (*flags) & LWS_FOP_FLAGS_MASK, 0664);
lws_fop_fd_t fop_fd;
if (ret < 0)
return NULL;
if (fstat(ret, &stat_buf) < 0)
goto bail;
fop_fd = malloc(sizeof(*fop_fd));
if (!fop_fd)
goto bail;
fop_fd->fops = fops;
fop_fd->flags = *flags;
fop_fd->fd = ret;
fop_fd->filesystem_priv = NULL; /* we don't use it */
fop_fd->len = stat_buf.st_size;
fop_fd->pos = 0;
return fop_fd;
bail:
close(ret);
return NULL;
}
LWS_VISIBLE int
_lws_plat_file_close(lws_fop_fd_t *fop_fd)
{
int fd = (*fop_fd)->fd;
free(*fop_fd);
*fop_fd = NULL;
return close(fd);
}
LWS_VISIBLE lws_fileofs_t
_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
{
lws_fileofs_t r;
if (offset > 0 && offset > fop_fd->len - fop_fd->pos)
offset = fop_fd->len - fop_fd->pos;
if ((lws_fileofs_t)fop_fd->pos + offset < 0)
offset = -fop_fd->pos;
r = lseek(fop_fd->fd, offset, SEEK_CUR);
if (r >= 0)
fop_fd->pos = r;
else
lwsl_err("error seeking from cur %ld, offset %ld\n",
(long)fop_fd->pos, (long)offset);
return r;
}
LWS_VISIBLE int
_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
long n;
n = read((int)fop_fd->fd, buf, len);
if (n == -1) {
*amount = 0;
return -1;
}
fop_fd->pos += n;
lwsl_debug("%s: read %ld of req %ld, pos %ld, len %ld\n", __func__, n,
(long)len, (long)fop_fd->pos, (long)fop_fd->len);
*amount = n;
return 0;
}
LWS_VISIBLE int
_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
long n;
n = write((int)fop_fd->fd, buf, len);
if (n == -1) {
*amount = 0;
return -1;
}
fop_fd->pos += n;
*amount = n;
return 0;
}
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
struct lws_context_creation_info *info)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n = context->count_threads, fd;
/* master context has the global fd lookup array */
context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
context->max_fds, "lws_lookup");
if (context->lws_lookup == NULL) {
lwsl_err("OOM on lws_lookup array for %d connections\n",
context->max_fds);
return 1;
}
lwsl_info(" mem: platform fd map: %5lu bytes\n",
(unsigned long)(sizeof(struct lws *) * context->max_fds));
fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
context->fd_random = fd;
if (context->fd_random < 0) {
lwsl_err("Unable to open random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, context->fd_random);
return 1;
}
if (!lws_libev_init_fd_table(context) &&
!lws_libuv_init_fd_table(context) &&
!lws_libevent_init_fd_table(context)) {
/* otherwise libev/uv/event handled it instead */
while (n--) {
if (pipe(pt->dummy_pipe_fds)) {
lwsl_err("Unable to create pipe\n");
return 1;
}
/* use the read end of pipe as first item */
pt->fds[0].fd = pt->dummy_pipe_fds[0];
pt->fds[0].events = LWS_POLLIN;
pt->fds[0].revents = 0;
pt->fds_count = 1;
pt++;
}
}
#ifdef LWS_WITH_PLUGINS
if (info->plugin_dirs)
lws_plat_plugins_init(context, info->plugin_dirs);
#endif
return 0;
}

745
thirdparty/lws/plat/lws-plat-win.c vendored Normal file
View File

@ -0,0 +1,745 @@
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#include "private-libwebsockets.h"
unsigned long long
time_in_microseconds()
{
#ifndef DELTA_EPOCH_IN_MICROSECS
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
FILETIME filetime;
ULARGE_INTEGER datetime;
#ifdef _WIN32_WCE
GetCurrentFT(&filetime);
#else
GetSystemTimeAsFileTime(&filetime);
#endif
/*
* As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
* ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
* prevent alignment faults on 64-bit Windows).
*/
memcpy(&datetime, &filetime, sizeof(datetime));
/* Windows file times are in 100s of nanoseconds. */
return (datetime.QuadPart - DELTA_EPOCH_IN_MICROSECS) / 10;
}
#ifdef _WIN32_WCE
time_t time(time_t *t)
{
time_t ret = time_in_microseconds() / 1000000;
if(t != NULL)
*t = ret;
return ret;
}
#endif
/* file descriptor hash management */
struct lws *
wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd)
{
int h = LWS_FD_HASH(fd);
int n = 0;
for (n = 0; n < context->fd_hashtable[h].length; n++)
if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd)
return context->fd_hashtable[h].wsi[n];
return NULL;
}
int
insert_wsi(struct lws_context *context, struct lws *wsi)
{
int h = LWS_FD_HASH(wsi->desc.sockfd);
if (context->fd_hashtable[h].length == (getdtablesize() - 1)) {
lwsl_err("hash table overflow\n");
return 1;
}
context->fd_hashtable[h].wsi[context->fd_hashtable[h].length++] = wsi;
return 0;
}
int
delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
{
int h = LWS_FD_HASH(fd);
int n = 0;
for (n = 0; n < context->fd_hashtable[h].length; n++)
if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) {
while (n < context->fd_hashtable[h].length) {
context->fd_hashtable[h].wsi[n] =
context->fd_hashtable[h].wsi[n + 1];
n++;
}
context->fd_hashtable[h].length--;
return 0;
}
lwsl_err("Failed to find fd %d requested for "
"delete in hashtable\n", fd);
return 1;
}
LWS_VISIBLE int lws_get_random(struct lws_context *context,
void *buf, int len)
{
int n;
char *p = (char *)buf;
for (n = 0; n < len; n++)
p[n] = (unsigned char)rand();
return n;
}
LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi)
{
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi->trunc_len)
return 1;
return (int)wsi->sock_send_blocking;
}
LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
{
fd_set readfds;
struct timeval tv = { 0, 0 };
assert((fd->events & LWS_POLLIN) == LWS_POLLIN);
FD_ZERO(&readfds);
FD_SET(fd->fd, &readfds);
return select(fd->fd + 1, &readfds, NULL, NULL, &tv);
}
LWS_VISIBLE void
lws_cancel_service(struct lws_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n = context->count_threads;
while (n--) {
WSASetEvent(pt->events[0]);
pt++;
}
}
LWS_VISIBLE void
lws_cancel_service_pt(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
WSASetEvent(pt->events[0]);
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
lwsl_emit_stderr(level, line);
}
LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{
struct lws_context_per_thread *pt;
WSANETWORKEVENTS networkevents;
struct lws_pollfd *pfd;
struct lws *wsi;
unsigned int i;
DWORD ev;
int n, m;
/* stay dead once we are dead */
if (context == NULL || !context->vhost_list)
return 1;
pt = &context->pt[tsi];
if (!context->service_tid_detected) {
struct lws _lws;
memset(&_lws, 0, sizeof(_lws));
_lws.context = context;
context->service_tid_detected = context->vhost_list->
protocols[0].callback(&_lws, LWS_CALLBACK_GET_THREAD_ID,
NULL, NULL, 0);
context->service_tid = context->service_tid_detected;
context->service_tid_detected = 1;
}
if (timeout_ms < 0)
{
if (lws_service_flag_pending(context, tsi)) {
/* any socket with events to service? */
for (n = 0; n < (int)pt->fds_count; n++) {
if (!pt->fds[n].revents)
continue;
m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
if (m < 0)
return -1;
/* if something closed, retry this slot */
if (m)
n--;
}
}
return 0;
}
for (i = 0; i < pt->fds_count; ++i) {
pfd = &pt->fds[i];
if (!(pfd->events & LWS_POLLOUT))
continue;
wsi = wsi_from_fd(context, pfd->fd);
if (wsi->listener)
continue;
if (!wsi || wsi->sock_send_blocking)
continue;
pfd->revents = LWS_POLLOUT;
n = lws_service_fd(context, pfd);
if (n < 0)
return -1;
/* if something closed, retry this slot */
if (n)
i--;
if (wsi->trunc_len)
WSASetEvent(pt->events[0]);
}
/*
* is there anybody with pending stuff that needs service forcing?
*/
if (!lws_service_adjust_timeout(context, 1, tsi)) {
/* -1 timeout means just do forced service */
_lws_plat_service_tsi(context, -1, pt->tid);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(context, 1, pt->tid))
/* yes... come back again quickly */
timeout_ms = 0;
}
ev = WSAWaitForMultipleEvents( 1, pt->events , FALSE, timeout_ms, FALSE);
if (ev == WSA_WAIT_EVENT_0) {
unsigned int eIdx;
WSAResetEvent(pt->events[0]);
for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) {
if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0, &networkevents) == SOCKET_ERROR) {
lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", LWS_ERRNO);
return -1;
}
pfd = &pt->fds[eIdx];
pfd->revents = (short)networkevents.lNetworkEvents;
if ((networkevents.lNetworkEvents & FD_CONNECT) &&
networkevents.iErrorCode[FD_CONNECT_BIT] &&
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EALREADY &&
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EINPROGRESS &&
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EWOULDBLOCK &&
networkevents.iErrorCode[FD_CONNECT_BIT] != WSAEINVAL) {
lwsl_debug("Unable to connect errno=%d\n",
networkevents.iErrorCode[FD_CONNECT_BIT]);
pfd->revents |= LWS_POLLHUP;
}
if (pfd->revents & LWS_POLLOUT) {
wsi = wsi_from_fd(context, pfd->fd);
if (wsi)
wsi->sock_send_blocking = 0;
}
/* if something closed, retry this slot */
if (pfd->revents & LWS_POLLHUP)
--eIdx;
if( pfd->revents != 0 ) {
lws_service_fd_tsi(context, pfd, tsi);
}
}
}
context->service_tid = 0;
if (ev == WSA_WAIT_TIMEOUT) {
lws_service_fd(context, NULL);
}
return 0;;
}
LWS_VISIBLE int
lws_plat_service(struct lws_context *context, int timeout_ms)
{
return _lws_plat_service_tsi(context, timeout_ms, 0);
}
LWS_VISIBLE int
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
{
int optval = 1;
int optlen = sizeof(optval);
u_long optl = 1;
DWORD dwBytesRet;
struct tcp_keepalive alive;
int protonbr;
#ifndef _WIN32_WCE
struct protoent *tcp_proto;
#endif
if (vhost->ka_time) {
/* enable keepalive on this socket */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
(const char *)&optval, optlen) < 0)
return 1;
alive.onoff = TRUE;
alive.keepalivetime = vhost->ka_time;
alive.keepaliveinterval = vhost->ka_interval;
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
NULL, 0, &dwBytesRet, NULL, NULL))
return 1;
}
/* Disable Nagle */
optval = 1;
#ifndef _WIN32_WCE
tcp_proto = getprotobyname("TCP");
if (!tcp_proto) {
lwsl_err("getprotobyname() failed with error %d\n", LWS_ERRNO);
return 1;
}
protonbr = tcp_proto->p_proto;
#else
protonbr = 6;
#endif
setsockopt(fd, protonbr, TCP_NODELAY, (const char *)&optval, optlen);
/* We are nonblocking... */
ioctlsocket(fd, FIONBIO, &optl);
return 0;
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
}
LWS_VISIBLE int
lws_plat_context_early_init(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro from Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (!err)
return 0;
/*
* Tell the user that we could not find a usable
* Winsock DLL
*/
lwsl_err("WSAStartup failed with error: %d\n", err);
return 1;
}
LWS_VISIBLE void
lws_plat_context_early_destroy(struct lws_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n = context->count_threads;
while (n--) {
if (pt->events) {
WSACloseEvent(pt->events[0]);
lws_free(pt->events);
}
pt++;
}
}
LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_context *context)
{
int n;
for (n = 0; n < FD_HASHTABLE_MODULUS; n++) {
if (context->fd_hashtable[n].wsi)
lws_free(context->fd_hashtable[n].wsi);
}
WSACleanup();
}
LWS_VISIBLE LWS_EXTERN int
lws_interface_to_sa(int ipv6,
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
{
#ifdef LWS_WITH_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
if (ipv6) {
if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
return 0;
}
}
#endif
long long address = inet_addr(ifname);
if (address == INADDR_NONE) {
struct hostent *entry = gethostbyname(ifname);
if (entry)
address = ((struct in_addr *)entry->h_addr_list[0])->s_addr;
}
if (address == INADDR_NONE)
return -1;
addr->sin_addr.s_addr = (lws_intptr_t)address;
return 0;
}
LWS_VISIBLE void
lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
pt->fds[pt->fds_count++].revents = 0;
pt->events[pt->fds_count] = pt->events[0];
WSAEventSelect(wsi->desc.sockfd, pt->events[0],
LWS_POLLIN | LWS_POLLHUP | FD_CONNECT);
}
LWS_VISIBLE void
lws_plat_delete_socket_from_fds(struct lws_context *context,
struct lws *wsi, int m)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
pt->events[m + 1] = pt->events[pt->fds_count--];
}
LWS_VISIBLE void
lws_plat_service_periodic(struct lws_context *context)
{
}
LWS_VISIBLE int
lws_plat_check_connection_error(struct lws *wsi)
{
int optVal;
int optLen = sizeof(int);
if (getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR,
(char*)&optVal, &optLen) != SOCKET_ERROR && optVal &&
optVal != LWS_EALREADY && optVal != LWS_EINPROGRESS &&
optVal != LWS_EWOULDBLOCK && optVal != WSAEINVAL) {
lwsl_debug("Connect failed SO_ERROR=%d\n", optVal);
return 1;
}
return 0;
}
LWS_VISIBLE int
lws_plat_change_pollfd(struct lws_context *context,
struct lws *wsi, struct lws_pollfd *pfd)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
long networkevents = LWS_POLLHUP | FD_CONNECT;
if ((pfd->events & LWS_POLLIN))
networkevents |= LWS_POLLIN;
if ((pfd->events & LWS_POLLOUT))
networkevents |= LWS_POLLOUT;
if (WSAEventSelect(wsi->desc.sockfd,
pt->events[0],
networkevents) != SOCKET_ERROR)
return 0;
lwsl_err("WSAEventSelect() failed with error %d\n", LWS_ERRNO);
return 1;
}
LWS_VISIBLE const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
{
WCHAR *buffer;
DWORD bufferlen = cnt;
BOOL ok = FALSE;
buffer = lws_malloc(bufferlen * 2, "inet_ntop");
if (!buffer) {
lwsl_err("Out of memory\n");
return NULL;
}
if (af == AF_INET) {
struct sockaddr_in srcaddr;
bzero(&srcaddr, sizeof(srcaddr));
srcaddr.sin_family = AF_INET;
memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr));
if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen))
ok = TRUE;
#ifdef LWS_WITH_IPV6
} else if (af == AF_INET6) {
struct sockaddr_in6 srcaddr;
bzero(&srcaddr, sizeof(srcaddr));
srcaddr.sin6_family = AF_INET6;
memcpy(&(srcaddr.sin6_addr), src, sizeof(srcaddr.sin6_addr));
if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen))
ok = TRUE;
#endif
} else
lwsl_err("Unsupported type\n");
if (!ok) {
int rv = WSAGetLastError();
lwsl_err("WSAAddressToString() : %d\n", rv);
} else {
if (WideCharToMultiByte(CP_ACP, 0, buffer, bufferlen, dst, cnt, 0, NULL) <= 0)
ok = FALSE;
}
lws_free(buffer);
return ok ? dst : NULL;
}
LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst)
{
WCHAR *buffer;
DWORD bufferlen = strlen(src) + 1;
BOOL ok = FALSE;
buffer = lws_malloc(bufferlen * 2, "inet_pton");
if (!buffer) {
lwsl_err("Out of memory\n");
return -1;
}
if (MultiByteToWideChar(CP_ACP, 0, src, bufferlen, buffer, bufferlen) <= 0) {
lwsl_err("Failed to convert multi byte to wide char\n");
lws_free(buffer);
return -1;
}
if (af == AF_INET) {
struct sockaddr_in dstaddr;
int dstaddrlen = sizeof(dstaddr);
bzero(&dstaddr, sizeof(dstaddr));
dstaddr.sin_family = AF_INET;
if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
ok = TRUE;
memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr));
}
#ifdef LWS_WITH_IPV6
} else if (af == AF_INET6) {
struct sockaddr_in6 dstaddr;
int dstaddrlen = sizeof(dstaddr);
bzero(&dstaddr, sizeof(dstaddr));
dstaddr.sin6_family = AF_INET6;
if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
ok = TRUE;
memcpy(dst, &dstaddr.sin6_addr, sizeof(dstaddr.sin6_addr));
}
#endif
} else
lwsl_err("Unsupported type\n");
if (!ok) {
int rv = WSAGetLastError();
lwsl_err("WSAAddressToString() : %d\n", rv);
}
lws_free(buffer);
return ok ? 1 : -1;
}
LWS_VISIBLE lws_fop_fd_t
_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
const char *vpath, lws_fop_flags_t *flags)
{
HANDLE ret;
WCHAR buf[MAX_PATH];
lws_fop_fd_t fop_fd;
LARGE_INTEGER llFileSize = {0};
MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, ARRAY_SIZE(buf));
if (((*flags) & 7) == _O_RDONLY) {
ret = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
} else {
ret = CreateFileW(buf, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
if (ret == LWS_INVALID_FILE)
goto bail;
fop_fd = malloc(sizeof(*fop_fd));
if (!fop_fd)
goto bail;
fop_fd->fops = fops;
fop_fd->fd = ret;
fop_fd->filesystem_priv = NULL; /* we don't use it */
fop_fd->flags = *flags;
fop_fd->len = GetFileSize(ret, NULL);
if(GetFileSizeEx(ret, &llFileSize))
fop_fd->len = llFileSize.QuadPart;
fop_fd->pos = 0;
return fop_fd;
bail:
return NULL;
}
LWS_VISIBLE int
_lws_plat_file_close(lws_fop_fd_t *fop_fd)
{
HANDLE fd = (*fop_fd)->fd;
free(*fop_fd);
*fop_fd = NULL;
CloseHandle((HANDLE)fd);
return 0;
}
LWS_VISIBLE lws_fileofs_t
_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
{
LARGE_INTEGER l;
l.QuadPart = offset;
return SetFilePointerEx((HANDLE)fop_fd->fd, l, NULL, FILE_CURRENT);
}
LWS_VISIBLE int
_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
DWORD _amount;
if (!ReadFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) {
*amount = 0;
return 1;
}
fop_fd->pos += _amount;
*amount = (unsigned long)_amount;
return 0;
}
LWS_VISIBLE int
_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t* buf, lws_filepos_t len)
{
DWORD _amount;
if (!WriteFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) {
*amount = 0;
return 1;
}
fop_fd->pos += _amount;
*amount = (unsigned long)_amount;
return 0;
}
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
struct lws_context_creation_info *info)
{
struct lws_context_per_thread *pt = &context->pt[0];
int i, n = context->count_threads;
for (i = 0; i < FD_HASHTABLE_MODULUS; i++) {
context->fd_hashtable[i].wsi =
lws_zalloc(sizeof(struct lws*) * context->max_fds, "win hashtable");
if (!context->fd_hashtable[i].wsi)
return -1;
}
while (n--) {
pt->events = lws_malloc(sizeof(WSAEVENT) *
(context->fd_limit_per_thread + 1), "event table");
if (pt->events == NULL) {
lwsl_err("Unable to allocate events array for %d connections\n",
context->fd_limit_per_thread + 1);
return 1;
}
pt->fds_count = 0;
pt->events[0] = WSACreateEvent();
pt++;
}
context->fd_random = 0;
#ifdef LWS_WITH_PLUGINS
if (info->plugin_dirs)
lws_plat_plugins_init(context, info->plugin_dirs);
#endif
return 0;
}
int kill(int pid, int sig)
{
lwsl_err("Sorry Windows doesn't support kill().");
exit(0);
}
int fork(void)
{
lwsl_err("Sorry Windows doesn't support fork().");
exit(0);
}

557
thirdparty/lws/pollfd.c vendored Normal file
View File

@ -0,0 +1,557 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
int
_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
{
struct lws_context_per_thread *pt;
struct lws_context *context;
int ret = 0, pa_events = 1;
struct lws_pollfd *pfd;
int sampled_tid, tid;
if (!wsi || wsi->position_in_fds_table < 0)
return 0;
if (wsi->handling_pollout && !_and && _or == LWS_POLLOUT) {
/*
* Happening alongside service thread handling POLLOUT.
* The danger is when he is finished, he will disable POLLOUT,
* countermanding what we changed here.
*
* Instead of changing the fds, inform the service thread
* what happened, and ask it to leave POLLOUT active on exit
*/
wsi->leave_pollout_active = 1;
/*
* by definition service thread is not in poll wait, so no need
* to cancel service
*/
lwsl_debug("%s: using leave_pollout_active\n", __func__);
return 0;
}
context = wsi->context;
pt = &context->pt[(int)wsi->tsi];
assert(wsi->position_in_fds_table >= 0 &&
wsi->position_in_fds_table < pt->fds_count);
pfd = &pt->fds[wsi->position_in_fds_table];
pa->fd = wsi->desc.sockfd;
pa->prev_events = pfd->events;
pa->events = pfd->events = (pfd->events & ~_and) | _or;
if (wsi->http2_substream)
return 0;
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
wsi->user_space, (void *)pa, 0)) {
ret = -1;
goto bail;
}
if (_and & LWS_POLLIN) {
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ);
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ);
}
if (_or & LWS_POLLIN) {
lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
}
if (_and & LWS_POLLOUT) {
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
}
if (_or & LWS_POLLOUT) {
lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_WRITE);
}
/*
* if we changed something in this pollfd...
* ... and we're running in a different thread context
* than the service thread...
* ... and the service thread is waiting ...
* then cancel it to force a restart with our changed events
*/
#if LWS_POSIX
pa_events = pa->prev_events != pa->events;
#endif
if (pa_events) {
if (lws_plat_change_pollfd(context, wsi, pfd)) {
lwsl_info("%s failed\n", __func__);
ret = -1;
goto bail;
}
sampled_tid = context->service_tid;
if (sampled_tid) {
tid = wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
if (tid == -1) {
ret = -1;
goto bail;
}
if (tid != sampled_tid)
lws_cancel_service_pt(wsi);
}
}
bail:
return ret;
}
#ifndef LWS_NO_SERVER
static void
lws_accept_modulation(struct lws_context_per_thread *pt, int allow)
{
// multithread listen seems broken
#if 0
struct lws_vhost *vh = context->vhost_list;
struct lws_pollargs pa1;
while (vh) {
if (allow)
_lws_change_pollfd(pt->wsi_listening,
0, LWS_POLLIN, &pa1);
else
_lws_change_pollfd(pt->wsi_listening,
LWS_POLLIN, 0, &pa1);
vh = vh->vhost_next;
}
#endif
}
#endif
int
insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 };
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int ret = 0;
lwsl_debug("%s: %p: tsi=%d, sock=%d, pos-in-fds=%d\n",
__func__, wsi, wsi->tsi, wsi->desc.sockfd, pt->fds_count);
if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) {
lwsl_err("Too many fds (%d vs %d)\n", context->max_fds,
context->fd_limit_per_thread );
return 1;
}
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
if (wsi->desc.sockfd >= context->max_fds) {
lwsl_err("Socket fd %d is too high (%d)\n",
wsi->desc.sockfd, context->max_fds);
return 1;
}
#endif
assert(wsi);
assert(wsi->vhost);
assert(lws_socket_is_valid(wsi->desc.sockfd));
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 1))
return -1;
lws_pt_lock(pt);
pt->count_conns++;
insert_wsi(context, wsi);
#if defined(LWS_WITH_ESP8266)
if (wsi->position_in_fds_table == -1)
#endif
wsi->position_in_fds_table = pt->fds_count;
pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
#if LWS_POSIX
pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
#else
pt->fds[wsi->position_in_fds_table].events = 0;
#endif
pa.events = pt->fds[pt->fds_count].events;
lws_plat_insert_socket_into_fds(context, wsi);
/* external POLL support via protocol 0 */
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
wsi->user_space, (void *) &pa, 0))
ret = -1;
#ifndef LWS_NO_SERVER
/* if no more room, defeat accepts on this thread */
if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1)
lws_accept_modulation(pt, 0);
#endif
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *)&pa, 1))
ret = -1;
return ret;
}
int
remove_wsi_socket_from_fds(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 };
#if !defined(LWS_WITH_ESP8266)
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws *end_wsi;
int v;
#endif
int m, ret = 0;
if (wsi->parent_carries_io) {
lws_same_vh_protocol_remove(wsi);
return 0;
}
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
if (wsi->desc.sockfd > context->max_fds) {
lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd,
context->max_fds);
return 1;
}
#endif
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *)&pa, 1))
return -1;
lws_same_vh_protocol_remove(wsi);
/* the guy who is to be deleted's slot index in pt->fds */
m = wsi->position_in_fds_table;
#if !defined(LWS_WITH_ESP8266)
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
LWS_EV_PREPARE_DELETION);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
LWS_EV_PREPARE_DELETION);
lws_pt_lock(pt);
lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n",
__func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table,
pt->fds_count, pt->fds[pt->fds_count].fd);
/* have the last guy take up the now vacant slot */
pt->fds[m] = pt->fds[pt->fds_count - 1];
#endif
/* this decrements pt->fds_count */
lws_plat_delete_socket_from_fds(context, wsi, m);
#if !defined(LWS_WITH_ESP8266)
v = (int) pt->fds[m].fd;
/* end guy's "position in fds table" is now the deletion guy's old one */
end_wsi = wsi_from_fd(context, v);
if (!end_wsi) {
lwsl_err("no wsi found for sock fd %d at pos %d, pt->fds_count=%d\n",
(int)pt->fds[m].fd, m, pt->fds_count);
assert(0);
} else
end_wsi->position_in_fds_table = m;
/* deletion guy's lws_lookup entry needs nuking */
delete_from_fd(context, wsi->desc.sockfd);
/* removed wsi has no position any more */
wsi->position_in_fds_table = -1;
/* remove also from external POLL support via protocol 0 */
if (lws_socket_is_valid(wsi->desc.sockfd))
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
wsi->user_space, (void *) &pa, 0))
ret = -1;
#ifndef LWS_NO_SERVER
if (!context->being_destroyed)
/* if this made some room, accept connects on this thread */
if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
lws_accept_modulation(pt, 1);
#endif
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 1))
ret = -1;
#endif
return ret;
}
int
lws_change_pollfd(struct lws *wsi, int _and, int _or)
{
struct lws_context_per_thread *pt;
struct lws_context *context;
struct lws_pollargs pa;
int ret = 0;
if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0)
return 1;
context = lws_get_context(wsi);
if (!context)
return 1;
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 0))
return -1;
pt = &context->pt[(int)wsi->tsi];
lws_pt_lock(pt);
ret = _lws_change_pollfd(wsi, _and, _or, &pa);
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 0))
ret = -1;
return ret;
}
LWS_VISIBLE int
lws_callback_on_writable(struct lws *wsi)
{
struct lws_context_per_thread *pt;
#ifdef LWS_WITH_HTTP2
struct lws *network_wsi, *wsi2;
int already;
#endif
int n;
if (wsi->state == LWSS_SHUTDOWN)
return 0;
if (wsi->socket_is_permanently_unusable)
return 0;
pt = &wsi->context->pt[(int)wsi->tsi];
if (wsi->parent_carries_io) {
#if defined(LWS_WITH_STATS)
if (!wsi->active_writable_req_us) {
wsi->active_writable_req_us = time_in_microseconds();
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
}
#endif
n = lws_callback_on_writable(wsi->parent);
if (n < 0)
return n;
wsi->parent_pending_cb_on_writable = 1;
return 1;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_REQ, 1);
#if defined(LWS_WITH_STATS)
if (!wsi->active_writable_req_us) {
wsi->active_writable_req_us = time_in_microseconds();
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
}
#endif
#ifdef LWS_WITH_HTTP2
lwsl_info("%s: %p\n", __func__, wsi);
if (wsi->mode != LWSCM_HTTP2_SERVING)
goto network_sock;
if (wsi->u.h2.requested_POLLOUT) {
lwsl_info("already pending writable\n");
return 1;
}
/* is this for DATA or for control messages? */
if (wsi->upgraded_to_http2 && !wsi->u.h2.h2n->pps &&
!lws_h2_tx_cr_get(wsi)) {
/*
* other side is not able to cope with us sending DATA
* anything so no matter if we have POLLOUT on our side if it's
* DATA we want to send.
*
* Delay waiting for our POLLOUT until peer indicates he has
* space for more using tx window command in http2 layer
*/
lwsl_notice("%s: %p: skint (%d)\n", __func__, wsi, wsi->u.h2.tx_cr);
wsi->u.h2.skint = 1;
return 0;
}
wsi->u.h2.skint = 0;
network_wsi = lws_get_network_wsi(wsi);
already = network_wsi->u.h2.requested_POLLOUT;
/* mark everybody above him as requesting pollout */
wsi2 = wsi;
while (wsi2) {
wsi2->u.h2.requested_POLLOUT = 1;
lwsl_info("mark %p pending writable\n", wsi2);
wsi2 = wsi2->u.h2.parent_wsi;
}
/* for network action, act only on the network wsi */
wsi = network_wsi;
if (already)
return 1;
network_sock:
#endif
if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
return 1;
if (wsi->position_in_fds_table < 0) {
lwsl_debug("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
return -1;
}
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
return -1;
return 1;
}
/*
* stitch protocol choice into the vh protocol linked list
* We always insert ourselves at the start of the list
*
* X <-> B
* X <-> pAn <-> pB
*
* Illegal to attach more than once without detach inbetween
*/
void
lws_same_vh_protocol_insert(struct lws *wsi, int n)
{
if (wsi->same_vh_protocol_prev || wsi->same_vh_protocol_next) {
lws_same_vh_protocol_remove(wsi);
lwsl_notice("Attempted to attach wsi twice to same vh prot\n");
}
wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n];
/* old first guy is our next */
wsi->same_vh_protocol_next = wsi->vhost->same_vh_protocol_list[n];
/* we become the new first guy */
wsi->vhost->same_vh_protocol_list[n] = wsi;
if (wsi->same_vh_protocol_next)
/* old first guy points back to us now */
wsi->same_vh_protocol_next->same_vh_protocol_prev =
&wsi->same_vh_protocol_next;
}
void
lws_same_vh_protocol_remove(struct lws *wsi)
{
/*
* detach ourselves from vh protocol list if we're on one
* A -> B -> C
* A -> C , or, B -> C, or A -> B
*
* OK to call on already-detached wsi
*/
lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi);
if (wsi->same_vh_protocol_prev) {
assert (*(wsi->same_vh_protocol_prev) == wsi);
lwsl_info("have prev %p, setting him to our next %p\n",
wsi->same_vh_protocol_prev,
wsi->same_vh_protocol_next);
/* guy who pointed to us should point to our next */
*(wsi->same_vh_protocol_prev) = wsi->same_vh_protocol_next;
}
/* our next should point back to our prev */
if (wsi->same_vh_protocol_next) {
wsi->same_vh_protocol_next->same_vh_protocol_prev =
wsi->same_vh_protocol_prev;
}
wsi->same_vh_protocol_prev = NULL;
wsi->same_vh_protocol_next = NULL;
}
LWS_VISIBLE int
lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
const struct lws_protocols *protocol)
{
struct lws *wsi;
if (protocol < vhost->protocols ||
protocol >= (vhost->protocols + vhost->count_protocols)) {
lwsl_err("%s: protocol %p is not from vhost %p (%p - %p)\n",
__func__, protocol, vhost->protocols, vhost,
(vhost->protocols + vhost->count_protocols));
return -1;
}
wsi = vhost->same_vh_protocol_list[protocol - vhost->protocols];
while (wsi) {
assert(wsi->protocol == protocol);
assert(*wsi->same_vh_protocol_prev == wsi);
if (wsi->same_vh_protocol_next)
assert(wsi->same_vh_protocol_next->same_vh_protocol_prev ==
&wsi->same_vh_protocol_next);
lws_callback_on_writable(wsi);
wsi = wsi->same_vh_protocol_next;
}
return 0;
}
LWS_VISIBLE int
lws_callback_on_writable_all_protocol(const struct lws_context *context,
const struct lws_protocols *protocol)
{
struct lws_vhost *vhost = context->vhost_list;
int n;
while (vhost) {
for (n = 0; n < vhost->count_protocols; n++)
if (protocol->callback ==
vhost->protocols[n].callback &&
!strcmp(protocol->name, vhost->protocols[n].name))
break;
if (n != vhost->count_protocols)
lws_callback_on_writable_all_protocol_vhost(
vhost, &vhost->protocols[n]);
vhost = vhost->vhost_next;
}
return 0;
}

2618
thirdparty/lws/private-libwebsockets.h vendored Normal file

File diff suppressed because it is too large Load Diff

669
thirdparty/lws/server/fops-zip.c vendored Normal file
View File

@ -0,0 +1,669 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Original code used in this source file:
*
* https://github.com/PerBothner/DomTerm.git @912add15f3d0aec
*
* ./lws-term/io.c
* ./lws-term/junzip.c
*
* Copyright (C) 2017 Per Bothner <per@bothner.com>
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* ( copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
* lws rewrite:
*
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include <zlib.h>
/*
* This code works with zip format containers which may have files compressed
* with gzip deflate (type 8) or store uncompressed (type 0).
*
* Linux zip produces such zipfiles by default, eg
*
* $ zip ../myzip.zip file1 file2 file3
*/
#define ZIP_COMPRESSION_METHOD_STORE 0
#define ZIP_COMPRESSION_METHOD_DEFLATE 8
typedef struct {
lws_filepos_t filename_start;
uint32_t crc32;
uint32_t comp_size;
uint32_t uncomp_size;
uint32_t offset;
uint32_t mod_time;
uint16_t filename_len;
uint16_t extra;
uint16_t method;
uint16_t file_com_len;
} lws_fops_zip_hdr_t;
typedef struct {
struct lws_fop_fd fop_fd; /* MUST BE FIRST logical fop_fd into
* file inside zip: fops_zip fops */
lws_fop_fd_t zip_fop_fd; /* logical fop fd on to zip file
* itself: using platform fops */
lws_fops_zip_hdr_t hdr;
z_stream inflate;
lws_filepos_t content_start;
lws_filepos_t exp_uncomp_pos;
union {
uint8_t trailer8[8];
uint32_t trailer32[2];
} u;
uint8_t rbuf[128]; /* decompression chunk size */
int entry_count;
unsigned int decompress:1; /* 0 = direct from file */
unsigned int add_gzip_container:1;
} *lws_fops_zip_t;
struct lws_plat_file_ops fops_zip;
#define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD))
static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 };
enum {
ZC_SIGNATURE = 0,
ZC_VERSION_MADE_BY = 4,
ZC_VERSION_NEEDED_TO_EXTRACT = 6,
ZC_GENERAL_PURPOSE_BIT_FLAG = 8,
ZC_COMPRESSION_METHOD = 10,
ZC_LAST_MOD_FILE_TIME = 12,
ZC_LAST_MOD_FILE_DATE = 14,
ZC_CRC32 = 16,
ZC_COMPRESSED_SIZE = 20,
ZC_UNCOMPRESSED_SIZE = 24,
ZC_FILE_NAME_LENGTH = 28,
ZC_EXTRA_FIELD_LENGTH = 30,
ZC_FILE_COMMENT_LENGTH = 32,
ZC_DISK_NUMBER_START = 34,
ZC_INTERNAL_FILE_ATTRIBUTES = 36,
ZC_EXTERNAL_FILE_ATTRIBUTES = 38,
ZC_REL_OFFSET_LOCAL_HEADER = 42,
ZC_DIRECTORY_LENGTH = 46,
ZE_SIGNATURE_OFFSET = 0,
ZE_DESK_NUMBER = 4,
ZE_CENTRAL_DIRECTORY_DISK_NUMBER = 6,
ZE_NUM_ENTRIES_THIS_DISK = 8,
ZE_NUM_ENTRIES = 10,
ZE_CENTRAL_DIRECTORY_SIZE = 12,
ZE_CENTRAL_DIR_OFFSET = 16,
ZE_ZIP_COMMENT_LENGTH = 20,
ZE_DIRECTORY_LENGTH = 22,
ZL_REL_OFFSET_CONTENT = 28,
ZL_HEADER_LENGTH = 30,
LWS_FZ_ERR_SEEK_END_RECORD = 1,
LWS_FZ_ERR_READ_END_RECORD,
LWS_FZ_ERR_END_RECORD_MAGIC,
LWS_FZ_ERR_END_RECORD_SANITY,
LWS_FZ_ERR_CENTRAL_SEEK,
LWS_FZ_ERR_CENTRAL_READ,
LWS_FZ_ERR_CENTRAL_SANITY,
LWS_FZ_ERR_NAME_TOO_LONG,
LWS_FZ_ERR_NAME_SEEK,
LWS_FZ_ERR_NAME_READ,
LWS_FZ_ERR_CONTENT_SANITY,
LWS_FZ_ERR_CONTENT_SEEK,
LWS_FZ_ERR_SCAN_SEEK,
LWS_FZ_ERR_NOT_FOUND,
LWS_FZ_ERR_ZLIB_INIT,
LWS_FZ_ERR_READ_CONTENT,
LWS_FZ_ERR_SEEK_COMPRESSED,
};
static uint16_t
get_u16(void *p)
{
const uint8_t *c = (const uint8_t *)p;
return (uint16_t)((c[0] | (c[1] << 8)));
}
static uint32_t
get_u32(void *p)
{
const uint8_t *c = (const uint8_t *)p;
return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
}
int
lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
{
lws_filepos_t amount;
uint8_t buf[96];
int i;
if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0)
return LWS_FZ_ERR_SEEK_END_RECORD;
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
ZE_DIRECTORY_LENGTH))
return LWS_FZ_ERR_READ_END_RECORD;
if (amount != ZE_DIRECTORY_LENGTH)
return LWS_FZ_ERR_READ_END_RECORD;
/*
* We require the zip to have the last record right at the end
* Linux zip always does this if no zip comment.
*/
if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6)
return LWS_FZ_ERR_END_RECORD_MAGIC;
i = get_u16(buf + ZE_NUM_ENTRIES);
if (get_u16(buf + ZE_DESK_NUMBER) ||
get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK))
return LWS_FZ_ERR_END_RECORD_SANITY;
/* end record is OK... look for our file in the central dir */
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0)
return LWS_FZ_ERR_CENTRAL_SEEK;
while (i--) {
priv->content_start = lws_vfs_tell(priv->zip_fop_fd);
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
ZC_DIRECTORY_LENGTH))
return LWS_FZ_ERR_CENTRAL_READ;
if (amount != ZC_DIRECTORY_LENGTH)
return LWS_FZ_ERR_CENTRAL_READ;
if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50)
return LWS_FZ_ERR_CENTRAL_SANITY;
lwsl_debug("cstart 0x%lx\n", (unsigned long)priv->content_start);
priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH);
priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH);
priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd);
priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD);
priv->hdr.crc32 = get_u32(buf + ZC_CRC32);
priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE);
priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE);
priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER);
priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME);
priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH);
if (priv->hdr.filename_len != len)
goto next;
if (len >= sizeof(buf) - 1)
return LWS_FZ_ERR_NAME_TOO_LONG;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
&amount, buf, len))
return LWS_FZ_ERR_NAME_READ;
if (amount != len)
return LWS_FZ_ERR_NAME_READ;
buf[len] = '\0';
lwsl_debug("check %s vs %s\n", buf, name);
if (strcmp((const char *)buf, name))
goto next;
/* we found a match */
if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset) < 0)
return LWS_FZ_ERR_NAME_SEEK;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
&amount, buf,
ZL_HEADER_LENGTH))
return LWS_FZ_ERR_NAME_READ;
if (amount != ZL_HEADER_LENGTH)
return LWS_FZ_ERR_NAME_READ;
priv->content_start = priv->hdr.offset +
ZL_HEADER_LENGTH +
priv->hdr.filename_len +
get_u16(buf + ZL_REL_OFFSET_CONTENT);
lwsl_debug("content supposed to start at 0x%lx\n",
(unsigned long)priv->content_start);
if (priv->content_start > priv->zip_fop_fd->len)
return LWS_FZ_ERR_CONTENT_SANITY;
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
priv->content_start) < 0)
return LWS_FZ_ERR_CONTENT_SEEK;
/* we are aligned at the start of the content */
priv->exp_uncomp_pos = 0;
return 0;
next:
if (i && lws_vfs_file_seek_set(priv->zip_fop_fd,
priv->content_start +
ZC_DIRECTORY_LENGTH +
priv->hdr.filename_len +
priv->hdr.extra +
priv->hdr.file_com_len) < 0)
return LWS_FZ_ERR_SCAN_SEEK;
}
return LWS_FZ_ERR_NOT_FOUND;
}
static int
lws_fops_zip_reset_inflate(lws_fops_zip_t priv)
{
if (priv->decompress)
inflateEnd(&priv->inflate);
priv->inflate.zalloc = Z_NULL;
priv->inflate.zfree = Z_NULL;
priv->inflate.opaque = Z_NULL;
priv->inflate.avail_in = 0;
priv->inflate.next_in = Z_NULL;
if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) {
lwsl_err("inflate init failed\n");
return LWS_FZ_ERR_ZLIB_INIT;
}
if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->content_start) < 0)
return LWS_FZ_ERR_CONTENT_SEEK;
priv->exp_uncomp_pos = 0;
return 0;
}
static lws_fop_fd_t
lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
const char *vpath, lws_fop_flags_t *flags)
{
lws_fop_flags_t local_flags = 0;
lws_fops_zip_t priv;
char rp[192];
int m;
/*
* vpath points at the / after the fops signature in vfs_path, eg
* with a vfs_path "/var/www/docs/manual.zip/index.html", vpath
* will come pointing at "/index.html"
*/
priv = lws_zalloc(sizeof(*priv), "fops_zip priv");
if (!priv)
return NULL;
priv->fop_fd.fops = &fops_zip;
m = sizeof(rp) - 1;
if ((vpath - vfs_path - 1) < m)
m = vpath - vfs_path - 1;
strncpy(rp, vfs_path, m);
rp[m] = '\0';
/* open the zip file itself using the incoming fops, not fops_zip */
priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags);
if (!priv->zip_fop_fd) {
lwsl_err("unable to open zip %s\n", rp);
goto bail1;
}
if (*vpath == '/')
vpath++;
m = lws_fops_zip_scan(priv, vpath, strlen(vpath));
if (m) {
lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
goto bail2;
}
/* the directory metadata tells us modification time, so pass it on */
priv->fop_fd.mod_time = priv->hdr.mod_time;
*flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
priv->fop_fd.flags = *flags;
/* The zip fop_fd is left pointing at the start of the content.
*
* 1) Content could be uncompressed (STORE), and we can always serve
* that directly
*
* 2) Content could be compressed (GZIP), and the client can handle
* receiving GZIP... we can wrap it in a GZIP header and trailer
* and serve the content part directly. The flag indicating we
* are providing GZIP directly is set so lws will send the right
* headers.
*
* 3) Content could be compressed (GZIP) but the client can't handle
* receiving GZIP... we can decompress it and serve as it is
* inflated piecemeal.
*
* 4) Content may be compressed some unknown way... fail
*
*/
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
/*
* it is stored uncompressed, leave it indicated as
* uncompressed, and just serve it from inside the
* zip with no gzip container;
*/
lwsl_info("direct zip serving (stored)\n");
priv->fop_fd.len = priv->hdr.uncomp_size;
return &priv->fop_fd;
}
if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
/*
* We can serve the gzipped file contents directly as gzip
* from inside the zip container; client says it is OK.
*
* To convert to standalone gzip, we have to add a 10-byte
* constant header and a variable 8-byte trailer around the
* content.
*
* The 8-byte trailer is prepared now and held in the priv.
*/
lwsl_info("direct zip serving (gzipped)\n");
priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
sizeof(priv->u);
if (lws_is_be()) {
uint8_t *p = priv->u.trailer8;
*p++ = (uint8_t)priv->hdr.crc32;
*p++ = (uint8_t)(priv->hdr.crc32 >> 8);
*p++ = (uint8_t)(priv->hdr.crc32 >> 16);
*p++ = (uint8_t)(priv->hdr.crc32 >> 24);
*p++ = (uint8_t)priv->hdr.uncomp_size;
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
*p = (uint8_t)(priv->hdr.uncomp_size >> 24);
} else {
priv->u.trailer32[0] = priv->hdr.crc32;
priv->u.trailer32[1] = priv->hdr.uncomp_size;
}
*flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
priv->fop_fd.flags = *flags;
priv->add_gzip_container = 1;
return &priv->fop_fd;
}
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
/* we must decompress it to serve it */
lwsl_info("decompressed zip serving\n");
priv->fop_fd.len = priv->hdr.uncomp_size;
if (lws_fops_zip_reset_inflate(priv)) {
lwsl_err("inflate init failed\n");
goto bail2;
}
priv->decompress = 1;
return &priv->fop_fd;
}
/* we can't handle it ... */
lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
priv->hdr.method);
bail2:
lws_vfs_file_close(&priv->zip_fop_fd);
bail1:
free(priv);
return NULL;
}
/* ie, we are closing the fop_fd for the file inside the gzip */
static int
lws_fops_zip_close(lws_fop_fd_t *fd)
{
lws_fops_zip_t priv = fop_fd_to_priv(*fd);
if (priv->decompress)
inflateEnd(&priv->inflate);
lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
free(priv);
*fd = NULL;
return 0;
}
static lws_fileofs_t
lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
{
fd->pos += offset_from_cur_pos;
return fd->pos;
}
static int
lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
lws_filepos_t len)
{
lws_fops_zip_t priv = fop_fd_to_priv(fd);
lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
int ret;
if (priv->decompress) {
if (priv->exp_uncomp_pos != fd->pos) {
/*
* there has been a seek in the uncompressed fop_fd
* we have to restart the decompression and loop eating
* the decompressed data up to the seek point
*/
lwsl_info("seek in decompressed\n");
lws_fops_zip_reset_inflate(priv);
while (priv->exp_uncomp_pos != fd->pos) {
rlen = len;
if (rlen > fd->pos - priv->exp_uncomp_pos)
rlen = fd->pos - priv->exp_uncomp_pos;
if (lws_fops_zip_read(fd, amount, buf, rlen))
return LWS_FZ_ERR_SEEK_COMPRESSED;
}
*amount = 0;
}
priv->inflate.avail_out = (unsigned int)len;
priv->inflate.next_out = buf;
spin:
if (!priv->inflate.avail_in) {
rlen = sizeof(priv->rbuf);
if (rlen > priv->hdr.comp_size -
(cur - priv->content_start))
rlen = priv->hdr.comp_size -
(priv->hdr.comp_size -
priv->content_start);
if (priv->zip_fop_fd->fops->LWS_FOP_READ(
priv->zip_fop_fd, &ramount, priv->rbuf,
rlen))
return LWS_FZ_ERR_READ_CONTENT;
cur += ramount;
priv->inflate.avail_in = (unsigned int)ramount;
priv->inflate.next_in = priv->rbuf;
}
ret = inflate(&priv->inflate, Z_NO_FLUSH);
if (ret == Z_STREAM_ERROR)
return ret;
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
/* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
return ret;
}
if (!priv->inflate.avail_in && priv->inflate.avail_out &&
cur != priv->content_start + priv->hdr.comp_size)
goto spin;
*amount = len - priv->inflate.avail_out;
priv->exp_uncomp_pos += *amount;
fd->pos += *amount;
return 0;
}
if (priv->add_gzip_container) {
lwsl_info("%s: gzip + container\n", __func__);
*amount = 0;
/* place the canned header at the start */
if (len && fd->pos < sizeof(hd)) {
rlen = sizeof(hd) - fd->pos;
if (rlen > len)
rlen = len;
/* provide stuff from canned header */
memcpy(buf, hd + fd->pos, (size_t)rlen);
fd->pos += rlen;
buf += rlen;
len -= rlen;
*amount += rlen;
}
/* serve gzipped data direct from zipfile */
if (len && fd->pos >= sizeof(hd) &&
fd->pos < priv->hdr.comp_size + sizeof(hd)) {
rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos -
priv->content_start);
if (rlen > len)
rlen = len;
if (rlen &&
priv->zip_fop_fd->pos < (priv->hdr.comp_size +
priv->content_start)) {
if (lws_vfs_file_read(priv->zip_fop_fd,
&ramount, buf, rlen))
return LWS_FZ_ERR_READ_CONTENT;
*amount += ramount;
fd->pos += ramount; // virtual pos
buf += ramount;
len -= ramount;
}
}
/* place the prepared trailer at the end */
if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) &&
fd->pos < priv->hdr.comp_size + sizeof(hd) +
sizeof(priv->u)) {
cur = fd->pos - priv->hdr.comp_size - sizeof(hd);
rlen = sizeof(priv->u) - cur;
if (rlen > len)
rlen = len;
memcpy(buf, priv->u.trailer8 + cur, (size_t)rlen);
*amount += rlen;
fd->pos += rlen;
}
return 0;
}
lwsl_info("%s: store\n", __func__);
if (len > priv->hdr.uncomp_size - (cur - priv->content_start))
len = priv->hdr.comp_size - (priv->hdr.comp_size -
priv->content_start);
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
amount, buf, len))
return LWS_FZ_ERR_READ_CONTENT;
return 0;
}
struct lws_plat_file_ops fops_zip = {
lws_fops_zip_open,
lws_fops_zip_close,
lws_fops_zip_seek_cur,
lws_fops_zip_read,
NULL,
{ { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
NULL,
};

929
thirdparty/lws/server/lejp-conf.c vendored Normal file
View File

@ -0,0 +1,929 @@
/*
* libwebsockets web server application
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include "../misc/lejp.h"
#ifndef _WIN32
/* this is needed for Travis CI */
#include <dirent.h>
#endif
#define ESC_INSTALL_DATADIR "_lws_ddir_"
static const char * const paths_global[] = {
"global.uid",
"global.gid",
"global.count-threads",
"global.init-ssl",
"global.server-string",
"global.plugin-dir",
"global.ws-pingpong-secs",
"global.timeout-secs",
"global.reject-service-keywords[].*",
"global.reject-service-keywords[]",
};
enum lejp_global_paths {
LEJPGP_UID,
LEJPGP_GID,
LEJPGP_COUNT_THREADS,
LWJPGP_INIT_SSL,
LEJPGP_SERVER_STRING,
LEJPGP_PLUGIN_DIR,
LWJPGP_PINGPONG_SECS,
LWJPGP_TIMEOUT_SECS,
LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
LWJPGP_REJECT_SERVICE_KEYWORDS
};
static const char * const paths_vhosts[] = {
"vhosts[]",
"vhosts[].mounts[]",
"vhosts[].name",
"vhosts[].port",
"vhosts[].interface",
"vhosts[].unix-socket",
"vhosts[].sts",
"vhosts[].host-ssl-key",
"vhosts[].host-ssl-cert",
"vhosts[].host-ssl-ca",
"vhosts[].access-log",
"vhosts[].mounts[].mountpoint",
"vhosts[].mounts[].origin",
"vhosts[].mounts[].protocol",
"vhosts[].mounts[].default",
"vhosts[].mounts[].auth-mask",
"vhosts[].mounts[].cgi-timeout",
"vhosts[].mounts[].cgi-env[].*",
"vhosts[].mounts[].cache-max-age",
"vhosts[].mounts[].cache-reuse",
"vhosts[].mounts[].cache-revalidate",
"vhosts[].mounts[].basic-auth",
"vhosts[].mounts[].cache-intermediaries",
"vhosts[].mounts[].extra-mimetypes.*",
"vhosts[].mounts[].interpret.*",
"vhosts[].ws-protocols[].*.*",
"vhosts[].ws-protocols[].*",
"vhosts[].ws-protocols[]",
"vhosts[].keepalive_timeout",
"vhosts[].enable-client-ssl",
"vhosts[].ciphers",
"vhosts[].ecdh-curve",
"vhosts[].noipv6",
"vhosts[].ipv6only",
"vhosts[].ssl-option-set",
"vhosts[].ssl-option-clear",
"vhosts[].mounts[].pmo[].*",
"vhosts[].headers[].*",
"vhosts[].headers[]",
"vhosts[].client-ssl-key",
"vhosts[].client-ssl-cert",
"vhosts[].client-ssl-ca",
"vhosts[].client-ssl-ciphers",
"vhosts[].onlyraw",
};
enum lejp_vhost_paths {
LEJPVP,
LEJPVP_MOUNTS,
LEJPVP_NAME,
LEJPVP_PORT,
LEJPVP_INTERFACE,
LEJPVP_UNIXSKT,
LEJPVP_STS,
LEJPVP_HOST_SSL_KEY,
LEJPVP_HOST_SSL_CERT,
LEJPVP_HOST_SSL_CA,
LEJPVP_ACCESS_LOG,
LEJPVP_MOUNTPOINT,
LEJPVP_ORIGIN,
LEJPVP_MOUNT_PROTOCOL,
LEJPVP_DEFAULT,
LEJPVP_DEFAULT_AUTH_MASK,
LEJPVP_CGI_TIMEOUT,
LEJPVP_CGI_ENV,
LEJPVP_MOUNT_CACHE_MAX_AGE,
LEJPVP_MOUNT_CACHE_REUSE,
LEJPVP_MOUNT_CACHE_REVALIDATE,
LEJPVP_MOUNT_BASIC_AUTH,
LEJPVP_MOUNT_CACHE_INTERMEDIARIES,
LEJPVP_MOUNT_EXTRA_MIMETYPES,
LEJPVP_MOUNT_INTERPRET,
LEJPVP_PROTOCOL_NAME_OPT,
LEJPVP_PROTOCOL_NAME,
LEJPVP_PROTOCOL,
LEJPVP_KEEPALIVE_TIMEOUT,
LEJPVP_ENABLE_CLIENT_SSL,
LEJPVP_CIPHERS,
LEJPVP_ECDH_CURVE,
LEJPVP_NOIPV6,
LEJPVP_IPV6ONLY,
LEJPVP_SSL_OPTION_SET,
LEJPVP_SSL_OPTION_CLEAR,
LEJPVP_PMO,
LEJPVP_HEADERS_NAME,
LEJPVP_HEADERS,
LEJPVP_CLIENT_SSL_KEY,
LEJPVP_CLIENT_SSL_CERT,
LEJPVP_CLIENT_SSL_CA,
LEJPVP_CLIENT_CIPHERS,
LEJPVP_FLAG_ONLYRAW,
};
static const char * const parser_errs[] = {
"",
"",
"No opening '{'",
"Expected closing '}'",
"Expected '\"'",
"String underrun",
"Illegal unescaped control char",
"Illegal escape format",
"Illegal hex number",
"Expected ':'",
"Illegal value start",
"Digit required after decimal point",
"Bad number format",
"Bad exponent format",
"Unknown token",
"Too many ']'",
"Mismatched ']'",
"Expected ']'",
"JSON nesting limit exceeded",
"Nesting tracking used up",
"Number too long",
"Comma or block end expected",
"Unknown",
"Parser callback errored (see earlier error)",
};
#define MAX_PLUGIN_DIRS 10
struct jpargs {
struct lws_context_creation_info *info;
struct lws_context *context;
const struct lws_protocols *protocols;
const struct lws_extension *extensions;
char *p, *end, valid;
struct lws_http_mount *head, *last;
struct lws_protocol_vhost_options *pvo;
struct lws_protocol_vhost_options *pvo_em;
struct lws_protocol_vhost_options *pvo_int;
struct lws_http_mount m;
const char **plugin_dirs;
int count_plugin_dirs;
unsigned int enable_client_ssl:1;
unsigned int fresh_mount:1;
unsigned int any_vhosts:1;
};
static void *
lwsws_align(struct jpargs *a)
{
if ((lws_intptr_t)(a->p) & 15)
a->p += 16 - ((lws_intptr_t)(a->p) & 15);
return a->p;
}
static int
arg_to_bool(const char *s)
{
static const char * const on[] = { "on", "yes", "true" };
int n = atoi(s);
if (n)
return 1;
for (n = 0; n < ARRAY_SIZE(on); n++)
if (!strcasecmp(s, on[n]))
return 1;
return 0;
}
static signed char
lejp_globals_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
struct lws_protocol_vhost_options *rej;
int n;
/* we only match on the prepared path strings */
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
/* this catches, eg, vhosts[].headers[].xxx */
if (reason == LEJPCB_VAL_STR_END &&
ctx->path_match == LWJPGP_REJECT_SERVICE_KEYWORDS_NAME + 1) {
rej = lwsws_align(a);
a->p += sizeof(*rej);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
rej->next = a->info->reject_service_keywords;
a->info->reject_service_keywords = rej;
rej->name = a->p;
lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf);
a->p += n - 1;
*(a->p++) = '\0';
rej->value = a->p;
rej->options = NULL;
goto dostring;
}
switch (ctx->path_match - 1) {
case LEJPGP_UID:
a->info->uid = atoi(ctx->buf);
return 0;
case LEJPGP_GID:
a->info->gid = atoi(ctx->buf);
return 0;
case LEJPGP_COUNT_THREADS:
a->info->count_threads = atoi(ctx->buf);
return 0;
case LWJPGP_INIT_SSL:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
return 0;
case LEJPGP_SERVER_STRING:
a->info->server_string = a->p;
break;
case LEJPGP_PLUGIN_DIR:
if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) {
lwsl_err("Too many plugin dirs\n");
return -1;
}
a->plugin_dirs[a->count_plugin_dirs++] = a->p;
break;
case LWJPGP_PINGPONG_SECS:
a->info->ws_ping_pong_interval = atoi(ctx->buf);
return 0;
case LWJPGP_TIMEOUT_SECS:
a->info->timeout_secs = atoi(ctx->buf);
return 0;
default:
return 0;
}
dostring:
a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
*(a->p)++ = '\0';
return 0;
}
static signed char
lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
struct lws_protocol_vhost_options *pvo, *mp_cgienv, *headers;
struct lws_http_mount *m;
char *p, *p1;
int n;
#if 0
lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
for (n = 0; n < ctx->wildcount; n++)
lwsl_notice(" %d\n", ctx->wild[n]);
#endif
if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
/* set the defaults for this vhost */
a->valid = 1;
a->head = NULL;
a->last = NULL;
a->info->port = 0;
a->info->iface = NULL;
a->info->protocols = a->protocols;
a->info->extensions = a->extensions;
a->info->ssl_cert_filepath = NULL;
a->info->ssl_private_key_filepath = NULL;
a->info->ssl_ca_filepath = NULL;
a->info->client_ssl_cert_filepath = NULL;
a->info->client_ssl_private_key_filepath = NULL;
a->info->client_ssl_ca_filepath = NULL;
a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-SHA384:"
"HIGH:!aNULL:!eNULL:!EXPORT:"
"!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
"!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
"!DHE-RSA-AES128-SHA256:"
"!AES128-GCM-SHA256:"
"!AES128-SHA256:"
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
a->info->timeout_secs = 5;
a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-SHA384:"
"HIGH:!aNULL:!eNULL:!EXPORT:"
"!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
"!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
"!DHE-RSA-AES128-SHA256:"
"!AES128-GCM-SHA256:"
"!AES128-SHA256:"
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
a->info->pvo = NULL;
a->info->headers = NULL;
a->info->keepalive_timeout = 5;
a->info->log_filepath = NULL;
a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK |
LWS_SERVER_OPTION_STS | LWS_SERVER_OPTION_ONLY_RAW);
a->enable_client_ssl = 0;
}
if (reason == LEJPCB_OBJECT_START &&
ctx->path_match == LEJPVP_MOUNTS + 1) {
a->fresh_mount = 1;
memset(&a->m, 0, sizeof(a->m));
}
/* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
if (reason == LEJPCB_OBJECT_START &&
ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
a->pvo = lwsws_align(a);
a->p += sizeof(*a->pvo);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
a->pvo->next = a->info->pvo;
a->info->pvo = a->pvo;
a->pvo->name = a->p;
lwsl_notice(" adding protocol %s\n", a->p);
a->p += n;
a->pvo->value = a->p;
a->pvo->options = NULL;
goto dostring;
}
/* this catches, eg, vhosts[].headers[].xxx */
if (reason == LEJPCB_VAL_STR_END &&
ctx->path_match == LEJPVP_HEADERS_NAME + 1) {
headers = lwsws_align(a);
a->p += sizeof(*headers);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
headers->next = a->info->headers;
a->info->headers = headers;
headers->name = a->p;
// lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf);
a->p += n - 1;
*(a->p++) = ':';
if (a->p < a->end)
*(a->p++) = '\0';
else
*(a->p - 1) = '\0';
headers->value = a->p;
headers->options = NULL;
goto dostring;
}
if (reason == LEJPCB_OBJECT_END &&
(ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
a->valid) {
struct lws_vhost *vhost;
//lwsl_notice("%s\n", ctx->path);
if (!a->info->port) {
lwsl_err("Port required (eg, 443)");
return 1;
}
a->valid = 0;
a->info->mounts = a->head;
vhost = lws_create_vhost(a->context, a->info);
if (!vhost) {
lwsl_err("Failed to create vhost %s\n",
a->info->vhost_name);
return 1;
}
a->any_vhosts = 1;
if (a->enable_client_ssl) {
const char *cert_filepath = a->info->client_ssl_cert_filepath;
const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
const char *ca_filepath = a->info->client_ssl_ca_filepath;
const char *cipher_list = a->info->client_ssl_cipher_list;
memset(a->info, 0, sizeof(*a->info));
a->info->client_ssl_cert_filepath = cert_filepath;
a->info->client_ssl_private_key_filepath = private_key_filepath;
a->info->client_ssl_ca_filepath = ca_filepath;
a->info->client_ssl_cipher_list = cipher_list;
a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
lws_init_vhost_client_ssl(a->info, vhost);
}
return 0;
}
if (reason == LEJPCB_OBJECT_END &&
ctx->path_match == LEJPVP_MOUNTS + 1) {
static const char * const mount_protocols[] = {
"http://",
"https://",
"file://",
"cgi://",
">http://",
">https://",
"callback://",
"gzip://",
};
if (!a->fresh_mount)
return 0;
if (!a->m.mountpoint || !a->m.origin) {
lwsl_err("mountpoint and origin required\n");
return 1;
}
lwsl_debug("adding mount %s\n", a->m.mountpoint);
m = lwsws_align(a);
memcpy(m, &a->m, sizeof(*m));
if (a->last)
a->last->mount_next = m;
for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
if (!strncmp(a->m.origin, mount_protocols[n],
strlen(mount_protocols[n]))) {
lwsl_info("----%s\n", a->m.origin);
m->origin_protocol = n;
m->origin = a->m.origin +
strlen(mount_protocols[n]);
break;
}
if (n == ARRAY_SIZE(mount_protocols)) {
lwsl_err("unsupported protocol:// %s\n", a->m.origin);
return 1;
}
a->p += sizeof(*m);
if (!a->head)
a->head = m;
a->last = m;
a->fresh_mount = 0;
}
/* we only match on the prepared path strings */
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
switch (ctx->path_match - 1) {
case LEJPVP_NAME:
a->info->vhost_name = a->p;
break;
case LEJPVP_PORT:
a->info->port = atoi(ctx->buf);
return 0;
case LEJPVP_INTERFACE:
a->info->iface = a->p;
break;
case LEJPVP_UNIXSKT:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK;
else
a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK);
return 0;
case LEJPVP_STS:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_STS;
else
a->info->options &= ~(LWS_SERVER_OPTION_STS);
return 0;
case LEJPVP_HOST_SSL_KEY:
a->info->ssl_private_key_filepath = a->p;
break;
case LEJPVP_HOST_SSL_CERT:
a->info->ssl_cert_filepath = a->p;
break;
case LEJPVP_HOST_SSL_CA:
a->info->ssl_ca_filepath = a->p;
break;
case LEJPVP_ACCESS_LOG:
a->info->log_filepath = a->p;
break;
case LEJPVP_MOUNTPOINT:
a->m.mountpoint = a->p;
a->m.mountpoint_len = (unsigned char)strlen(ctx->buf);
break;
case LEJPVP_ORIGIN:
if (!strncmp(ctx->buf, "callback://", 11))
a->m.protocol = a->p + 11;
if (!a->m.origin)
a->m.origin = a->p;
break;
case LEJPVP_DEFAULT:
a->m.def = a->p;
break;
case LEJPVP_DEFAULT_AUTH_MASK:
a->m.auth_mask = atoi(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_MAX_AGE:
a->m.cache_max_age = atoi(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_REUSE:
a->m.cache_reusable = arg_to_bool(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_REVALIDATE:
a->m.cache_revalidate = arg_to_bool(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_INTERMEDIARIES:
a->m.cache_intermediaries = arg_to_bool(ctx->buf);;
return 0;
case LEJPVP_MOUNT_BASIC_AUTH:
a->m.basic_auth_login_file = a->p;
break;
case LEJPVP_CGI_TIMEOUT:
a->m.cgi_timeout = atoi(ctx->buf);
return 0;
case LEJPVP_KEEPALIVE_TIMEOUT:
a->info->keepalive_timeout = atoi(ctx->buf);
return 0;
case LEJPVP_CLIENT_CIPHERS:
a->info->client_ssl_cipher_list = a->p;
break;
case LEJPVP_CIPHERS:
a->info->ssl_cipher_list = a->p;
break;
case LEJPVP_ECDH_CURVE:
a->info->ecdh_curve = a->p;
break;
case LEJPVP_PMO:
case LEJPVP_CGI_ENV:
mp_cgienv = lwsws_align(a);
a->p += sizeof(*a->m.cgienv);
mp_cgienv->next = a->m.cgienv;
a->m.cgienv = mp_cgienv;
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
mp_cgienv->name = a->p;
a->p += n;
mp_cgienv->value = a->p;
mp_cgienv->options = NULL;
//lwsl_notice(" adding pmo / cgi-env '%s' = '%s'\n", mp_cgienv->name,
// mp_cgienv->value);
goto dostring;
case LEJPVP_PROTOCOL_NAME_OPT:
/* this catches, eg,
* vhosts[].ws-protocols[].xxx-protocol.yyy-option
* ie, these are options attached to a protocol with { }
*/
pvo = lwsws_align(a);
a->p += sizeof(*a->pvo);
n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
pvo->next = a->pvo->options;
a->pvo->options = pvo;
pvo->name = a->p;
a->p += n;
pvo->value = a->p;
pvo->options = NULL;
break;
case LEJPVP_MOUNT_EXTRA_MIMETYPES:
a->pvo_em = lwsws_align(a);
a->p += sizeof(*a->pvo_em);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
a->pvo_em->next = a->m.extra_mimetypes;
a->m.extra_mimetypes = a->pvo_em;
a->pvo_em->name = a->p;
lwsl_notice(" adding extra-mimetypes %s -> %s\n", a->p, ctx->buf);
a->p += n;
a->pvo_em->value = a->p;
a->pvo_em->options = NULL;
break;
case LEJPVP_MOUNT_INTERPRET:
a->pvo_int = lwsws_align(a);
a->p += sizeof(*a->pvo_int);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
a->pvo_int->next = a->m.interpret;
a->m.interpret = a->pvo_int;
a->pvo_int->name = a->p;
lwsl_notice(" adding interpret %s -> %s\n", a->p,
ctx->buf);
a->p += n;
a->pvo_int->value = a->p;
a->pvo_int->options = NULL;
break;
case LEJPVP_ENABLE_CLIENT_SSL:
a->enable_client_ssl = arg_to_bool(ctx->buf);
return 0;
case LEJPVP_CLIENT_SSL_KEY:
a->info->client_ssl_private_key_filepath = a->p;
break;
case LEJPVP_CLIENT_SSL_CERT:
a->info->client_ssl_cert_filepath = a->p;
break;
case LEJPVP_CLIENT_SSL_CA:
a->info->client_ssl_ca_filepath = a->p;
break;
case LEJPVP_NOIPV6:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6;
else
a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6);
return 0;
case LEJPVP_FLAG_ONLYRAW:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_ONLY_RAW;
else
a->info->options &= ~(LWS_SERVER_OPTION_ONLY_RAW);
return 0;
case LEJPVP_IPV6ONLY:
a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY;
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE;
else
a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
return 0;
case LEJPVP_SSL_OPTION_SET:
a->info->ssl_options_set |= atol(ctx->buf);
return 0;
case LEJPVP_SSL_OPTION_CLEAR:
a->info->ssl_options_clear |= atol(ctx->buf);
return 0;
default:
return 0;
}
dostring:
p = ctx->buf;
p1 = strstr(p, ESC_INSTALL_DATADIR);
if (p1) {
n = p1 - p;
if (n > a->end - a->p)
n = a->end - a->p;
strncpy(a->p, p, n);
a->p += n;
a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
p += n + strlen(ESC_INSTALL_DATADIR);
}
a->p += lws_snprintf(a->p, a->end - a->p, "%s", p);
*(a->p)++ = '\0';
return 0;
}
/*
* returns 0 = OK, 1 = can't open, 2 = parsing error
*/
static int
lwsws_get_config(void *user, const char *f, const char * const *paths,
int count_paths, lejp_callback cb)
{
unsigned char buf[128];
struct lejp_ctx ctx;
int n, m, fd;
fd = open(f, O_RDONLY);
if (fd < 0) {
lwsl_err("Cannot open %s\n", f);
return 2;
}
lwsl_info("%s: %s\n", __func__, f);
lejp_construct(&ctx, cb, user, paths, count_paths);
do {
n = read(fd, buf, sizeof(buf));
if (!n)
break;
m = (int)(signed char)lejp_parse(&ctx, buf, n);
} while (m == LEJP_CONTINUE);
close(fd);
n = ctx.line;
lejp_destruct(&ctx);
if (m < 0) {
lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m,
parser_errs[-m]);
return 2;
}
return 0;
}
#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0
static int
lwsws_get_config_d(void *user, const char *d, const char * const *paths,
int count_paths, lejp_callback cb)
{
uv_dirent_t dent;
uv_fs_t req;
char path[256];
int ret = 0, ir;
uv_loop_t loop;
ir = uv_loop_init(&loop);
if (ir) {
lwsl_err("%s: loop init failed %d\n", __func__, ir);
}
if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) {
lwsl_err("Scandir on %s failed\n", d);
return 2;
}
while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
ret = lwsws_get_config(user, path, paths, count_paths, cb);
if (ret)
goto bail;
}
bail:
uv_fs_req_cleanup(&req);
while (uv_loop_close(&loop))
;
return ret;
}
#else
#ifndef _WIN32
static int filter(const struct dirent *ent)
{
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
return 0;
return 1;
}
#endif
static int
lwsws_get_config_d(void *user, const char *d, const char * const *paths,
int count_paths, lejp_callback cb)
{
#ifndef _WIN32
struct dirent **namelist;
char path[256];
int n, i, ret = 0;
n = scandir(d, &namelist, filter, alphasort);
if (n < 0) {
lwsl_err("Scandir on %s failed\n", d);
return 1;
}
for (i = 0; i < n; i++) {
if (strchr(namelist[i]->d_name, '~'))
goto skip;
lws_snprintf(path, sizeof(path) - 1, "%s/%s", d,
namelist[i]->d_name);
ret = lwsws_get_config(user, path, paths, count_paths, cb);
if (ret) {
while (i++ < n)
free(namelist[i]);
goto bail;
}
skip:
free(namelist[i]);
}
bail:
free(namelist);
return ret;
#else
return 0;
#endif
}
#endif
int
lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
char **cs, int *len)
{
struct jpargs a;
const char * const *old = info->plugin_dirs;
char dd[128];
memset(&a, 0, sizeof(a));
a.info = info;
a.p = *cs;
a.end = (a.p + *len) - 1;
a.valid = 0;
lwsws_align(&a);
info->plugin_dirs = (void *)a.p;
a.plugin_dirs = (void *)a.p; /* writeable version */
a.p += MAX_PLUGIN_DIRS * sizeof(void *);
/* copy any default paths */
while (old && *old) {
a.plugin_dirs[a.count_plugin_dirs++] = *old;
old++;
}
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
if (lwsws_get_config(&a, dd, paths_global,
ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
if (lwsws_get_config_d(&a, dd, paths_global,
ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
a.plugin_dirs[a.count_plugin_dirs] = NULL;
*cs = a.p;
*len = a.end - a.p;
return 0;
}
int
lwsws_get_config_vhosts(struct lws_context *context,
struct lws_context_creation_info *info, const char *d,
char **cs, int *len)
{
struct jpargs a;
char dd[128];
memset(&a, 0, sizeof(a));
a.info = info;
a.p = *cs;
a.end = a.p + *len;
a.valid = 0;
a.context = context;
a.protocols = info->protocols;
a.extensions = info->extensions;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
if (lwsws_get_config(&a, dd, paths_vhosts,
ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
if (lwsws_get_config_d(&a, dd, paths_vhosts,
ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
*cs = a.p;
*len = a.end - a.p;
if (!a.any_vhosts) {
lwsl_err("Need at least one vhost\n");
return 1;
}
// lws_finalize_startup(context);
return 0;
}

1783
thirdparty/lws/server/parsers.c vendored Normal file

File diff suppressed because it is too large Load Diff

214
thirdparty/lws/server/ranges.c vendored Normal file
View File

@ -0,0 +1,214 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* RFC7233 ranges parser
*
* Copyright (C) 2016 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
/*
* RFC7233 examples
*
* o The first 500 bytes (byte offsets 0-499, inclusive):
*
* bytes=0-499
*
* o The second 500 bytes (byte offsets 500-999, inclusive):
*
* bytes=500-999
*
* o The final 500 bytes (byte offsets 9500-9999, inclusive):
*
* bytes=-500
*
* Or:
*
* bytes=9500-
*
* o The first and last bytes only (bytes 0 and 9999):
*
* bytes=0-0,-1
*
* o Other valid (but not canonical) specifications of the second 500
* bytes (byte offsets 500-999, inclusive):
*
* bytes=500-600,601-999
* bytes=500-700,601-999
*/
/*
* returns 1 if the range struct represents a usable range
* if no ranges header, you get one of these for the whole
* file. Otherwise you get one for each valid range in the
* header.
*
* returns 0 if no further valid range forthcoming; rp->state
* may be LWSRS_SYNTAX or LWSRS_COMPLETED
*/
int
lws_ranges_next(struct lws_range_parsing *rp)
{
static const char * const beq = "bytes=";
char c;
while (1) {
c = rp->buf[rp->pos];
switch (rp->state) {
case LWSRS_SYNTAX:
case LWSRS_COMPLETED:
return 0;
case LWSRS_NO_ACTIVE_RANGE:
rp->state = LWSRS_COMPLETED;
return 0;
case LWSRS_BYTES_EQ: // looking for "bytes="
if (c != beq[rp->pos]) {
rp->state = LWSRS_SYNTAX;
return -1;
}
if (rp->pos == 5)
rp->state = LWSRS_FIRST;
break;
case LWSRS_FIRST:
rp->start = 0;
rp->end = 0;
rp->start_valid = 0;
rp->end_valid = 0;
rp->state = LWSRS_STARTING;
// fallthru
case LWSRS_STARTING:
if (c == '-') {
rp->state = LWSRS_ENDING;
break;
}
if (!(c >= '0' && c <= '9')) {
rp->state = LWSRS_SYNTAX;
return 0;
}
rp->start = (rp->start * 10) + (c - '0');
rp->start_valid = 1;
break;
case LWSRS_ENDING:
if (c == ',' || c == '\0') {
rp->state = LWSRS_FIRST;
if (c == ',')
rp->pos++;
/*
* By the end of this, start and end are
* always valid if the range still is
*/
if (!rp->start_valid) { /* eg, -500 */
if (rp->end > rp->extent)
rp->end = rp->extent;
rp->start = rp->extent - rp->end;
rp->end = rp->extent - 1;
} else
if (!rp->end_valid)
rp->end = rp->extent - 1;
rp->did_try = 1;
/* end must be >= start or ignore it */
if (rp->end < rp->start) {
if (c == ',')
break;
rp->state = LWSRS_COMPLETED;
return 0;
}
return 1; /* issue range */
}
if (!(c >= '0' && c <= '9')) {
rp->state = LWSRS_SYNTAX;
return 0;
}
rp->end = (rp->end * 10) + (c - '0');
rp->end_valid = 1;
break;
}
rp->pos++;
}
}
void
lws_ranges_reset(struct lws_range_parsing *rp)
{
rp->pos = 0;
rp->ctr = 0;
rp->start = 0;
rp->end = 0;
rp->start_valid = 0;
rp->end_valid = 0;
rp->state = LWSRS_BYTES_EQ;
}
/*
* returns count of valid ranges
*/
int
lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
unsigned long long extent)
{
rp->agg = 0;
rp->send_ctr = 0;
rp->inside = 0;
rp->count_ranges = 0;
rp->did_try = 0;
lws_ranges_reset(rp);
rp->state = LWSRS_COMPLETED;
rp->extent = extent;
if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf),
WSI_TOKEN_HTTP_RANGE) <= 0)
return 0;
rp->state = LWSRS_BYTES_EQ;
while (lws_ranges_next(rp)) {
rp->count_ranges++;
rp->agg += rp->end - rp->start + 1;
}
lwsl_debug("%s: count %d\n", __func__, rp->count_ranges);
lws_ranges_reset(rp);
if (rp->did_try && !rp->count_ranges)
return -1; /* "not satisfiable */
lws_ranges_next(rp);
return rp->count_ranges;
}

360
thirdparty/lws/server/server-handshake.c vendored Normal file
View File

@ -0,0 +1,360 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
#ifndef LWS_NO_EXTENSIONS
static int
lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char ext_name[64], *args, *end = (*p) + budget - 1;
const struct lws_ext_options *opts, *po;
const struct lws_extension *ext;
struct lws_ext_option_arg oa;
int n, m, more = 1;
int ext_count = 0;
char ignore;
char *c;
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list
*/
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
return 0;
/*
* break down the list of client extensions
* and go through them
*/
if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
WSI_TOKEN_EXTENSIONS) < 0)
return 1;
c = (char *)pt->serv_buf;
lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
wsi->count_act_ext = 0;
ignore = 0;
n = 0;
args = NULL;
/*
* We may get a simple request
*
* Sec-WebSocket-Extensions: permessage-deflate
*
* or an elaborated one with requested options
*
* Sec-WebSocket-Extensions: permessage-deflate; \
* server_no_context_takeover; \
* client_no_context_takeover
*/
while (more) {
if (*c && (*c != ',' && *c != '\t')) {
if (*c == ';') {
ignore = 1;
args = c + 1;
}
if (ignore || *c == ' ') {
c++;
continue;
}
ext_name[n] = *c++;
if (n < sizeof(ext_name) - 1)
n++;
continue;
}
ext_name[n] = '\0';
ignore = 0;
if (!*c)
more = 0;
else {
c++;
if (!n)
continue;
}
while (args && *args && *args == ' ')
args++;
/* check a client's extension against our support */
ext = wsi->vhost->extensions;
while (ext && ext->callback) {
if (strcmp(ext_name, ext->name)) {
ext++;
continue;
}
/*
* oh, we do support this one he asked for... but let's
* confirm he only gave it once
*/
for (m = 0; m < wsi->count_act_ext; m++)
if (wsi->active_extensions[m] == ext) {
lwsl_info("extension mentioned twice\n");
return 1; /* shenanigans */
}
/*
* ask user code if it's OK to apply it on this
* particular connection + protocol
*/
m = (wsi->protocol->callback)(wsi,
LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
wsi->user_space, ext_name, 0);
/*
* zero return from callback means go ahead and allow
* the extension, it's what we get if the callback is
* unhandled
*/
if (m) {
ext++;
continue;
}
/* apply it */
ext_count++;
/* instantiate the extension on this conn */
wsi->active_extensions[wsi->count_act_ext] = ext;
/* allow him to construct his context */
if (ext->callback(lws_get_context(wsi), ext, wsi,
LWS_EXT_CB_CONSTRUCT,
(void *)&wsi->act_ext_user[
wsi->count_act_ext],
(void *)&opts, 0)) {
lwsl_info("ext %s failed construction\n",
ext_name);
ext_count--;
ext++;
continue;
}
if (ext_count > 1)
*(*p)++ = ',';
else
LWS_CPYAPP(*p,
"\x0d\x0aSec-WebSocket-Extensions: ");
*p += lws_snprintf(*p, (end - *p), "%s", ext_name);
/*
* go through the options trying to apply the
* recognized ones
*/
lwsl_debug("ext args %s", args);
while (args && *args && *args != ',') {
while (*args == ' ')
args++;
po = opts;
while (po->name) {
lwsl_debug("'%s' '%s'\n", po->name, args);
/* only support arg-less options... */
if (po->type == EXTARG_NONE &&
!strncmp(args, po->name,
strlen(po->name))) {
oa.option_name = NULL;
oa.option_index = po - opts;
oa.start = NULL;
lwsl_debug("setting %s\n", po->name);
if (!ext->callback(
lws_get_context(wsi), ext, wsi,
LWS_EXT_CB_OPTION_SET,
wsi->act_ext_user[
wsi->count_act_ext],
&oa, (end - *p))) {
*p += lws_snprintf(*p, (end - *p), "; %s", po->name);
lwsl_debug("adding option %s\n", po->name);
}
}
po++;
}
while (*args && *args != ',' && *args != ';')
args++;
}
wsi->count_act_ext++;
lwsl_parser("count_act_ext <- %d\n",
wsi->count_act_ext);
ext++;
}
n = 0;
args = NULL;
}
return 0;
}
#endif
int
handshake_0405(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_process_html_args args;
unsigned char hash[20];
int n, accept_len;
char *response;
char *p;
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
!lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
lwsl_parser("handshake_04 missing pieces\n");
/* completed header processing, but missing some bits */
goto bail;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) {
lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
goto bail;
}
/*
* since key length is restricted above (currently 128), cannot
* overflow
*/
n = sprintf((char *)pt->serv_buf,
"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
lws_SHA1(pt->serv_buf, n, hash);
accept_len = lws_b64_encode_string((char *)hash, 20,
(char *)pt->serv_buf, context->pt_serv_buf_size);
if (accept_len < 0) {
lwsl_warn("Base64 encoded hash too long\n");
goto bail;
}
/* allocate the per-connection user memory (if any) */
if (lws_ensure_user_space(wsi))
goto bail;
/* create the response packet */
/* make a buffer big enough for everything */
response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
p = response;
LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
"Upgrade: WebSocket\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Sec-WebSocket-Accept: ");
strcpy(p, (char *)pt->serv_buf);
p += accept_len;
/* we can only return the protocol header if:
* - one came in, and ... */
if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
/* - it is not an empty string */
wsi->protocol->name &&
wsi->protocol->name[0]) {
LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
}
#ifndef LWS_NO_EXTENSIONS
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list.
*
* Give him a limited write bugdet
*/
if (lws_extension_server_handshake(wsi, &p, 192))
goto bail;
#endif
LWS_CPYAPP(p, "\x0d\x0a");
args.p = p;
args.max_len = ((char *)pt->serv_buf + context->pt_serv_buf_size) - p;
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_ADD_HEADERS,
wsi->user_space, &args, 0))
goto bail;
p = args.p;
/* end of response packet */
LWS_CPYAPP(p, "\x0d\x0a");
if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
response, p - response)) {
/* okay send the handshake response accepting the connection */
lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266)
fwrite(response, 1, p - response, stderr);
#endif
n = lws_write(wsi, (unsigned char *)response,
p - response, LWS_WRITE_HTTP_HEADERS);
if (n != (p - response)) {
lwsl_debug("handshake_0405: ERROR writing to socket\n");
goto bail;
}
}
/* alright clean up and set ourselves into established state */
wsi->state = LWSS_ESTABLISHED;
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
{
const char * uri_ptr =
lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
const struct lws_http_mount *hit =
lws_find_mount(wsi, uri_ptr, uri_len);
if (hit && hit->cgienv &&
wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
wsi->user_space, (void *)hit->cgienv, 0))
return 1;
}
return 0;
bail:
/* caller will free up his parsing allocations */
return -1;
}

3025
thirdparty/lws/server/server.c vendored Normal file

File diff suppressed because it is too large Load Diff

477
thirdparty/lws/server/ssl-server.c vendored Normal file
View File

@ -0,0 +1,477 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
extern int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
extern void
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
#if !defined(LWS_WITH_MBEDTLS)
static int
OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
SSL *ssl;
int n;
struct lws *wsi;
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
/*
* !!! nasty openssl requires the index to come as a library-scope
* static
*/
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
n = wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
x509_ctx, ssl, preverify_ok);
/* convert return code from 0 = OK to 1 = OK */
return !n;
}
#endif
static int
lws_context_ssl_init_ecdh(struct lws_vhost *vhost)
{
#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
EC_KEY *EC_key = NULL;
EVP_PKEY *pkey;
int KeyType;
X509 *x;
if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH))
return 0;
lwsl_notice(" Using ECDH certificate support\n");
/* Get X509 certificate from ssl context */
x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0);
if (!x) {
lwsl_err("%s: x is NULL\n", __func__);
return 1;
}
/* Get the public key from certificate */
pkey = X509_get_pubkey(x);
if (!pkey) {
lwsl_err("%s: pkey is NULL\n", __func__);
return 1;
}
/* Get the key type */
KeyType = EVP_PKEY_type(pkey->type);
if (EVP_PKEY_EC != KeyType) {
lwsl_notice("Key type is not EC\n");
return 0;
}
/* Get the key */
EC_key = EVP_PKEY_get1_EC_KEY(pkey);
/* Set ECDH parameter */
if (!EC_key) {
lwsl_err("%s: ECDH key is NULL \n", __func__);
return 1;
}
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key);
EC_KEY_free(EC_key);
#endif
return 0;
}
static int
lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
#if defined(LWS_HAVE_OPENSSL_ECDH_H) && !defined(LWS_WITH_MBEDTLS)
EC_KEY *ecdh;
int ecdh_nid;
const char *ecdh_curve = "prime256v1";
if (info->ecdh_curve)
ecdh_curve = info->ecdh_curve;
ecdh_nid = OBJ_sn2nid(ecdh_curve);
if (NID_undef == ecdh_nid) {
lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve);
return 1;
}
ecdh = EC_KEY_new_by_curve_name(ecdh_nid);
if (NULL == ecdh) {
lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve);
return 1;
}
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh);
EC_KEY_free(ecdh);
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve);
#else
#if !defined(LWS_WITH_MBEDTLS)
lwsl_notice(" OpenSSL doesn't support ECDH\n");
#endif
#endif
return 0;
}
#if !defined(LWS_WITH_MBEDTLS) && defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT)
static int
lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
{
struct lws_context *context = (struct lws_context *)arg;
struct lws_vhost *vhost, *vh;
const char *servername;
if (!ssl)
return SSL_TLSEXT_ERR_NOACK;
/*
* We can only get ssl accepted connections by using a vhost's ssl_ctx
* find out which listening one took us and only match vhosts on the
* same port.
*/
vh = context->vhost_list;
while (vh) {
if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
break;
vh = vh->vhost_next;
}
if (!vh) {
assert(vh); /* can't match the incoming vh? */
return SSL_TLSEXT_ERR_OK;
}
servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (!servername) {
/* the client doesn't know what hostname it wants */
lwsl_info("SNI: Unknown ServerName: %s\n", servername);
return SSL_TLSEXT_ERR_OK;
}
vhost = lws_select_vhost(context, vh->listen_port, servername);
if (!vhost) {
lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
return SSL_TLSEXT_ERR_OK;
}
lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port);
/* select the ssl ctx from the selected vhost for this conn */
SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
return SSL_TLSEXT_ERR_OK;
}
#endif
LWS_VISIBLE int
lws_context_init_server_ssl(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
struct lws_context *context = vhost->context;
struct lws wsi;
unsigned long error;
int n;
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
vhost->use_ssl = 0;
return 0;
}
/*
* If he is giving a cert filepath, take it as a sign he wants to use
* it on this vhost. User code can leave the cert filepath NULL and
* set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in
* which case he's expected to set up the cert himself at
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
* provides the vhost SSL_CTX * in the user parameter.
*/
if (info->ssl_cert_filepath)
info->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX;
if (info->port != CONTEXT_PORT_NO_LISTEN) {
vhost->use_ssl = lws_check_opt(info->options,
LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX);
if (vhost->use_ssl && info->ssl_cipher_list)
lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
if (vhost->use_ssl)
lwsl_notice(" Using SSL mode\n");
else
lwsl_notice(" Using non-SSL mode\n");
}
/*
* give him a fake wsi with context + vhost set, so he can use
* lws_get_context() in the callback
*/
memset(&wsi, 0, sizeof(wsi));
wsi.vhost = vhost;
wsi.context = context;
(void)n;
(void)error;
/*
* Firefox insists on SSLv23 not SSLv3
* Konq disables SSLv2 by default now, SSLv23 works
*
* SSLv23_server_method() is the openssl method for "allow all TLS
* versions", compared to e.g. TLSv1_2_server_method() which only allows
* tlsv1.2. Unwanted versions must be disabled using SSL_CTX_set_options()
*/
#if !defined(LWS_WITH_MBEDTLS)
{
SSL_METHOD *method;
method = (SSL_METHOD *)SSLv23_server_method();
if (!method) {
error = ERR_get_error();
lwsl_err("problem creating ssl method %lu: %s\n",
error, ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!vhost->ssl_ctx) {
error = ERR_get_error();
lwsl_err("problem creating ssl context %lu: %s\n",
error, ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
}
#else
{
const SSL_METHOD *method = TLSv1_2_server_method();
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!vhost->ssl_ctx) {
lwsl_err("problem creating ssl context\n");
return 1;
}
}
#endif
#if !defined(LWS_WITH_MBEDTLS)
/* associate the lws context with the SSL_CTX */
SSL_CTX_set_ex_data(vhost->ssl_ctx,
openssl_SSL_CTX_private_data_index, (char *)vhost->context);
/* Disable SSLv2 and SSLv3 */
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION);
#endif
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
if (info->ssl_cipher_list)
SSL_CTX_set_cipher_list(vhost->ssl_ctx,
info->ssl_cipher_list);
#endif
/* as a server, are we requiring clients to identify themselves? */
if (lws_check_opt(info->options,
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) {
int verify_options = SSL_VERIFY_PEER;
if (!lws_check_opt(info->options,
LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
#if !defined(LWS_WITH_MBEDTLS)
SSL_CTX_set_session_id_context(vhost->ssl_ctx,
(unsigned char *)context, sizeof(void *));
/* absolutely require the client cert */
SSL_CTX_set_verify(vhost->ssl_ctx,
verify_options, OpenSSL_verify_callback);
#endif
}
#if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT)
SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
lws_ssl_server_name_cb);
SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, context);
#endif
/*
* give user code a chance to load certs into the server
* allowing it to verify incoming client certs
*/
#if !defined(LWS_WITH_MBEDTLS)
if (info->ssl_ca_filepath &&
!SSL_CTX_load_verify_locations(vhost->ssl_ctx,
info->ssl_ca_filepath, NULL)) {
lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__);
}
#endif
if (vhost->use_ssl) {
if (lws_context_ssl_init_ecdh_curve(info, vhost))
return -1;
vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
vhost->ssl_ctx, NULL, 0);
}
if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
/* Normally SSL listener rejects non-ssl, optionally allow */
vhost->allow_non_ssl_on_ssl_port = 1;
if (info->ssl_options_set)
SSL_CTX_set_options(vhost->ssl_ctx, info->ssl_options_set);
/* SSL_clear_options introduced in 0.9.8m */
#if !defined(LWS_WITH_MBEDTLS)
#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL)
if (info->ssl_options_clear)
SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear);
#endif
#endif
lwsl_info(" SSL options 0x%lX\n", SSL_CTX_get_options(vhost->ssl_ctx));
if (vhost->use_ssl && info->ssl_cert_filepath) {
/*
* The user code can choose to either pass the cert and
* key filepaths using the info members like this, or it can
* leave them NULL; force the vhost SSL_CTX init using the info
* options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
* set up the cert himself using the user callback
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
* happened just above and has the vhost SSL_CTX * in the user
* parameter.
*/
#if !defined(LWS_WITH_MBEDTLS)
/* set the local certificate from CertFile */
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx,
info->ssl_cert_filepath);
if (n != 1) {
error = ERR_get_error();
lwsl_err("problem getting cert '%s' %lu: %s\n",
info->ssl_cert_filepath,
error,
ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
lws_ssl_bind_passphrase(vhost->ssl_ctx, info);
#else
uint8_t *p;
lws_filepos_t flen;
int err;
if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p,
&flen)) {
lwsl_err("couldn't find cert file %s\n",
info->ssl_cert_filepath);
return 1;
}
err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p);
if (!err) {
lwsl_err("Problem loading cert\n");
return 1;
}
#if !defined(LWS_WITH_ESP32)
free(p);
p = NULL;
#endif
if (info->ssl_private_key_filepath) {
if (alloc_pem_to_der_file(vhost->context,
info->ssl_private_key_filepath, &p, &flen)) {
lwsl_err("couldn't find cert file %s\n",
info->ssl_cert_filepath);
return 1;
}
err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
if (!err) {
lwsl_err("Problem loading key\n");
return 1;
}
}
#if !defined(LWS_WITH_ESP32)
free(p);
p = NULL;
#endif
#endif
if (info->ssl_private_key_filepath != NULL) {
#if !defined(LWS_WITH_MBEDTLS)
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx,
info->ssl_private_key_filepath,
SSL_FILETYPE_PEM) != 1) {
error = ERR_get_error();
lwsl_err("ssl problem getting key '%s' %lu: %s\n",
info->ssl_private_key_filepath, error,
ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
#endif
} else
if (vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
vhost->ssl_ctx, NULL, 0)) {
lwsl_err("ssl private key not set\n");
return 1;
}
#if !defined(LWS_WITH_MBEDTLS)
/* verify private key */
if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
lwsl_err("Private SSL key doesn't match cert\n");
return 1;
}
#endif
}
if (vhost->use_ssl) {
if (lws_context_ssl_init_ecdh(vhost))
return 1;
/*
* SSL is happy and has a cert it's content with
* If we're supporting HTTP2, initialize that
*/
lws_context_init_http2_ssl(vhost);
}
return 0;
}

1699
thirdparty/lws/service.c vendored Normal file

File diff suppressed because it is too large Load Diff

976
thirdparty/lws/ssl.c vendored Normal file
View File

@ -0,0 +1,976 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
/* workaround for mingw */
#if !defined(ECONNABORTED)
#define ECONNABORTED 103
#endif
int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
lws_filepos_t len;
lws_fop_flags_t flags = LWS_O_RDONLY;
lws_fop_fd_t fops_fd = lws_vfs_file_open(
lws_get_fops(context), filename, &flags);
int ret = 1;
if (!fops_fd)
return 1;
len = lws_vfs_get_length(fops_fd);
*buf = lws_malloc((size_t)len, "lws_alloc_vfs_file");
if (!*buf)
goto bail;
if (lws_vfs_file_read(fops_fd, amount, *buf, len))
goto bail;
ret = 0;
bail:
lws_vfs_file_close(&fops_fd);
return ret;
}
#if defined(LWS_WITH_MBEDTLS)
#if defined(LWS_WITH_ESP32)
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
nvs_handle nvh;
size_t s;
int n = 0;
ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
n = 1;
goto bail;
}
*buf = lws_malloc(s, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
bail:
nvs_close(nvh);
return n;
}
#else
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
FILE *f;
size_t s;
int n = 0;
f = fopen(filename, "rb");
if (f == NULL) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_END) != 0) {
n = 1;
goto bail;
}
s = ftell(f);
if (s == -1) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_SET) != 0) {
n = 1;
goto bail;
}
*buf = lws_malloc(s, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (fread(*buf, s, 1, f) != 1) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
bail:
if (f)
fclose(f);
return n;
}
#endif
int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
uint8_t *pem, *p, *q, *end;
lws_filepos_t len;
int n;
n = alloc_file(context, filename, &pem, &len);
if (n)
return n;
/* trim the first line */
p = pem;
end = p + len;
if (strncmp((char *)p, "-----", 5))
goto bail;
p += 5;
while (p < end && *p != '\n' && *p != '-')
p++;
if (*p != '-')
goto bail;
while (p < end && *p != '\n')
p++;
if (p >= end)
goto bail;
p++;
/* trim the last line */
q = end - 2;
while (q > pem && *q != '\n')
q--;
if (*q != '\n')
goto bail;
*q = '\0';
*amount = lws_b64_decode_string((char *)p, (char *)pem, len);
*buf = pem;
return 0;
bail:
lws_free(pem);
return 4;
}
#endif
int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
int lws_ssl_get_error(struct lws *wsi, int n)
{
int m;
if (!wsi->ssl)
return 99;
m = SSL_get_error(wsi->ssl, n);
lwsl_debug("%s: %p %d -> %d\n", __func__, wsi->ssl, n, m);
return m;
}
/* Copies a string describing the code returned by lws_ssl_get_error(),
* which may also contain system error information in the case of SSL_ERROR_SYSCALL,
* into buf up to len.
* Returns a pointer to buf.
*
* Note: the lws_ssl_get_error() code is *not* an error code that can be passed
* to ERR_error_string(),
*
* ret is the return value originally passed to lws_ssl_get_error(), needed to disambiguate
* SYS_ERROR_SYSCALL.
*
* See man page for SSL_get_error().
*
* Not thread safe, uses strerror()
*/
char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) {
switch (status) {
case SSL_ERROR_NONE: return strncpy(buf, "SSL_ERROR_NONE", len);
case SSL_ERROR_ZERO_RETURN: return strncpy(buf, "SSL_ERROR_ZERO_RETURN", len);
case SSL_ERROR_WANT_READ: return strncpy(buf, "SSL_ERROR_WANT_READ", len);
case SSL_ERROR_WANT_WRITE: return strncpy(buf, "SSL_ERROR_WANT_WRITE", len);
case SSL_ERROR_WANT_CONNECT: return strncpy(buf, "SSL_ERROR_WANT_CONNECT", len);
case SSL_ERROR_WANT_ACCEPT: return strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len);
case SSL_ERROR_WANT_X509_LOOKUP: return strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len);
case SSL_ERROR_SYSCALL:
switch (ret) {
case 0:
lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF");
return buf;
case -1:
#ifndef LWS_PLAT_OPTEE
lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", strerror(errno));
#else
lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno);
#endif
return buf;
default:
return strncpy(buf, "SSL_ERROR_SYSCALL", len);
}
case SSL_ERROR_SSL: return "SSL_ERROR_SSL";
default: return "SSL_ERROR_UNKNOWN";
}
}
void
lws_ssl_elaborate_error(void)
{
#if defined(LWS_WITH_MBEDTLS)
#else
char buf[256];
u_long err;
while ((err = ERR_get_error()) != 0) {
ERR_error_string_n(err, buf, sizeof(buf));
lwsl_info("*** %s\n", buf);
}
#endif
}
#if !defined(LWS_WITH_MBEDTLS)
static int
lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata)
{
struct lws_context_creation_info * info =
(struct lws_context_creation_info *)userdata;
strncpy(buf, info->ssl_private_key_password, size);
buf[size - 1] = '\0';
return strlen(buf);
}
void
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info)
{
if (!info->ssl_private_key_password)
return;
/*
* password provided, set ssl callback and user data
* for checking password which will be trigered during
* SSL_CTX_use_PrivateKey_file function
*/
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info);
SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb);
}
#endif
int
lws_context_init_ssl_library(struct lws_context_creation_info *info)
{
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
lwsl_info(" Compiled with CyaSSL support\n");
#else
lwsl_info(" Compiled with wolfSSL support\n");
#endif
#else
#if defined(LWS_WITH_BORINGSSL)
lwsl_info(" Compiled with BoringSSL support\n");
#else
#if defined(LWS_WITH_MBEDTLS)
lwsl_info(" Compiled with MbedTLS support\n");
#else
lwsl_info(" Compiled with OpenSSL support\n");
#endif
#endif
#endif
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
return 0;
}
/* basic openssl init */
lwsl_info("Doing SSL library init\n");
#if !defined(LWS_WITH_MBEDTLS)
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
openssl_websocket_private_data_index =
SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL);
openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0,
NULL, NULL, NULL, NULL);
#endif
return 0;
}
LWS_VISIBLE void
lws_ssl_destroy(struct lws_vhost *vhost)
{
if (!lws_check_opt(vhost->context->options,
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return;
if (vhost->ssl_ctx)
SSL_CTX_free(vhost->ssl_ctx);
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
SSL_CTX_free(vhost->ssl_client_ctx);
#if defined(LWS_WITH_MBEDTLS)
if (vhost->x509_client_CA)
X509_free(vhost->x509_client_CA);
#else
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
// <= 1.0.1f = old api, 1.0.1g+ = new api
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
ERR_remove_state(0);
#else
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
!defined(LIBRESSL_VERSION_NUMBER) && \
!defined(OPENSSL_IS_BORINGSSL)
ERR_remove_thread_state();
#else
ERR_remove_thread_state(NULL);
#endif
#endif
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
SSL_COMP_free_compression_methods();
#endif
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
#endif
#endif
}
int
lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws *wsi, *wsi_next;
wsi = pt->pending_read_list;
while (wsi) {
wsi_next = wsi->pending_read_list_next;
pt->fds[wsi->position_in_fds_table].revents |=
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
return 1;
wsi = wsi_next;
}
return 0;
}
LWS_VISIBLE void
lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
if (!wsi->pending_read_list_prev &&
!wsi->pending_read_list_next &&
pt->pending_read_list != wsi)
/* we are not on the list */
return;
/* point previous guy's next to our next */
if (!wsi->pending_read_list_prev)
pt->pending_read_list = wsi->pending_read_list_next;
else
wsi->pending_read_list_prev->pending_read_list_next =
wsi->pending_read_list_next;
/* point next guy's previous to our previous */
if (wsi->pending_read_list_next)
wsi->pending_read_list_next->pending_read_list_prev =
wsi->pending_read_list_prev;
wsi->pending_read_list_prev = NULL;
wsi->pending_read_list_next = NULL;
}
LWS_VISIBLE int
lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n = 0, m;
if (!wsi->ssl)
return lws_ssl_capable_read_no_ssl(wsi, buf, len);
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
errno = 0;
n = SSL_read(wsi->ssl, buf, len);
#if defined(LWS_WITH_ESP32)
if (!n && errno == ENOTCONN) {
lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
return LWS_SSL_CAPABLE_ERROR;
}
#endif
#if defined(LWS_WITH_STATS)
if (!wsi->seen_rx) {
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_RX_DELAY,
time_in_microseconds() - wsi->accept_start_us);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
wsi->seen_rx = 1;
}
#endif
lwsl_debug("%p: SSL_read says %d\n", wsi, n);
/* manpage: returning 0 means connection shut down */
if (!n) {
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
}
if (n < 0) {
m = lws_ssl_get_error(wsi, n);
lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
if (m == SSL_ERROR_ZERO_RETURN ||
m == SSL_ERROR_SYSCALL)
return LWS_SSL_CAPABLE_ERROR;
if (SSL_want_read(wsi->ssl)) {
lwsl_debug("%s: WANT_READ\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
if (SSL_want_write(wsi->ssl)) {
lwsl_debug("%s: WANT_WRITE\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
}
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_restart_ws_ping_pong_timer(wsi);
/*
* if it was our buffer that limited what we read,
* check if SSL has additional data pending inside SSL buffers.
*
* Because these won't signal at the network layer with POLLIN
* and if we don't realize, this data will sit there forever
*/
if (n != len)
goto bail;
if (!wsi->ssl)
goto bail;
if (!SSL_pending(wsi->ssl))
goto bail;
if (wsi->pending_read_list_next)
return n;
if (wsi->pending_read_list_prev)
return n;
if (pt->pending_read_list == wsi)
return n;
/* add us to the linked list of guys with pending ssl */
if (pt->pending_read_list)
pt->pending_read_list->pending_read_list_prev = wsi;
wsi->pending_read_list_next = pt->pending_read_list;
wsi->pending_read_list_prev = NULL;
pt->pending_read_list = wsi;
return n;
bail:
lws_ssl_remove_wsi_from_buffered_list(wsi);
return n;
}
LWS_VISIBLE int
lws_ssl_pending(struct lws *wsi)
{
if (!wsi->ssl)
return 0;
return SSL_pending(wsi->ssl);
}
LWS_VISIBLE int
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
{
int n, m;
if (!wsi->ssl)
return lws_ssl_capable_write_no_ssl(wsi, buf, len);
n = SSL_write(wsi->ssl, buf, len);
if (n > 0)
return n;
m = lws_ssl_get_error(wsi, n);
if (m != SSL_ERROR_SYSCALL) {
if (SSL_want_read(wsi->ssl)) {
lwsl_notice("%s: want read\n", __func__);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
if (SSL_want_write(wsi->ssl)) {
lws_set_blocking_send(wsi);
lwsl_notice("%s: want write\n", __func__);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
}
lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL));
lws_ssl_elaborate_error();
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
}
static int
lws_gate_accepts(struct lws_context *context, int on)
{
struct lws_vhost *v = context->vhost_list;
lwsl_info("gating accepts %d\n", on);
context->ssl_gate_accepts = !on;
#if defined(LWS_WITH_STATS)
context->updated = 1;
#endif
while (v) {
if (v->use_ssl && v->lserv_wsi) /* gate ability to accept incoming connections */
if (lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on,
(LWS_POLLIN) * on))
lwsl_info("Unable to set accept POLLIN %d\n", on);
v = v->vhost_next;
}
return 0;
}
void
lws_ssl_info_callback(const SSL *ssl, int where, int ret)
{
struct lws *wsi;
struct lws_context *context;
struct lws_ssl_info si;
context = (struct lws_context *)SSL_CTX_get_ex_data(
SSL_get_SSL_CTX(ssl),
openssl_SSL_CTX_private_data_index);
if (!context)
return;
wsi = wsi_from_fd(context, SSL_get_fd(ssl));
if (!wsi)
return;
if (!(where & wsi->vhost->ssl_info_event_mask))
return;
si.where = where;
si.ret = ret;
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_SSL_INFO,
wsi->user_space, &si, 0))
lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
}
LWS_VISIBLE int
lws_ssl_close(struct lws *wsi)
{
lws_sockfd_type n;
if (!wsi->ssl)
return 0; /* not handled */
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
/* kill ssl callbacks, becausse we will remove the fd from the
* table linking it to the wsi
*/
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, NULL);
#endif
n = SSL_get_fd(wsi->ssl);
if (!wsi->socket_is_permanently_unusable)
SSL_shutdown(wsi->ssl);
compatible_close(n);
SSL_free(wsi->ssl);
wsi->ssl = NULL;
if (wsi->context->simultaneous_ssl_restriction &&
wsi->context->simultaneous_ssl-- ==
wsi->context->simultaneous_ssl_restriction)
/* we made space and can do an accept */
lws_gate_accepts(wsi->context, 1);
#if defined(LWS_WITH_STATS)
wsi->context->updated = 1;
#endif
return 1; /* handled */
}
/* leave all wsi close processing to the caller */
LWS_VISIBLE int
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
{
struct lws_context *context = wsi->context;
struct lws_vhost *vh;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n, m;
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
BIO *bio;
#endif
char buf[256];
(void)buf;
if (!LWS_SSL_ENABLED(wsi->vhost))
return 0;
switch (wsi->mode) {
case LWSCM_SSL_INIT:
case LWSCM_SSL_INIT_RAW:
if (wsi->ssl)
lwsl_err("%s: leaking ssl\n", __func__);
if (accept_fd == LWS_SOCK_INVALID)
assert(0);
if (context->simultaneous_ssl_restriction &&
context->simultaneous_ssl >= context->simultaneous_ssl_restriction) {
lwsl_notice("unable to deal with SSL connection\n");
return 1;
}
errno = 0;
wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
if (wsi->ssl == NULL) {
lwsl_err("SSL_new failed: %d (errno %d)\n",
lws_ssl_get_error(wsi, 0), errno);
lws_ssl_elaborate_error();
if (accept_fd != LWS_SOCK_INVALID)
compatible_close(accept_fd);
goto fail;
}
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
#endif
if (context->simultaneous_ssl_restriction &&
++context->simultaneous_ssl == context->simultaneous_ssl_restriction)
/* that was the last allowed SSL connection */
lws_gate_accepts(context, 0);
#if defined(LWS_WITH_STATS)
context->updated = 1;
#endif
#if !defined(LWS_WITH_MBEDTLS)
SSL_set_ex_data(wsi->ssl,
openssl_websocket_private_data_index, wsi);
#endif
SSL_set_fd(wsi->ssl, accept_fd);
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#else
#if defined(LWS_WITH_MBEDTLS)
lws_plat_set_socket_options(wsi->vhost, accept_fd);
#else
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
bio = SSL_get_rbio(wsi->ssl);
if (bio)
BIO_set_nbio(bio, 1); /* nonblocking */
else
lwsl_notice("NULL rbio\n");
bio = SSL_get_wbio(wsi->ssl);
if (bio)
BIO_set_nbio(bio, 1); /* nonblocking */
else
lwsl_notice("NULL rbio\n");
#endif
#endif
/*
* we are not accepted yet, but we need to enter ourselves
* as a live connection. That way we can retry when more
* pieces come if we're not sorted yet
*/
if (wsi->mode == LWSCM_SSL_INIT)
wsi->mode = LWSCM_SSL_ACK_PENDING;
else
wsi->mode = LWSCM_SSL_ACK_PENDING_RAW;
if (insert_wsi_socket_into_fds(context, wsi)) {
lwsl_err("%s: failed to insert into fds\n", __func__);
goto fail;
}
lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
context->timeout_secs);
lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n");
/* fallthru */
case LWSCM_SSL_ACK_PENDING:
case LWSCM_SSL_ACK_PENDING_RAW:
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_err("%s: lws_change_pollfd failed\n", __func__);
goto fail;
}
lws_latency_pre(context, wsi);
if (wsi->vhost->allow_non_ssl_on_ssl_port) {
n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
context->pt_serv_buf_size, MSG_PEEK);
/*
* optionally allow non-SSL connect on SSL listening socket
* This is disabled by default, if enabled it goes around any
* SSL-level access control (eg, client-side certs) so leave
* it disabled unless you know it's not a problem for you
*/
if (n >= 1 && pt->serv_buf[0] >= ' ') {
/*
* TLS content-type for Handshake is 0x16, and
* for ChangeCipherSpec Record, it's 0x14
*
* A non-ssl session will start with the HTTP
* method in ASCII. If we see it's not a legit
* SSL handshake kill the SSL for this
* connection and try to handle as a HTTP
* connection upgrade directly.
*/
wsi->use_ssl = 0;
SSL_shutdown(wsi->ssl);
SSL_free(wsi->ssl);
wsi->ssl = NULL;
if (lws_check_opt(context->options,
LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
wsi->redirect_to_https = 1;
goto accepted;
}
if (!n) /*
* connection is gone, or nothing to read
* if it's gone, we will timeout on
* PENDING_TIMEOUT_SSL_ACCEPT
*/
break;
if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK)) {
/*
* well, we get no way to know ssl or not
* so go around again waiting for something
* to come and give us a hint, or timeout the
* connection.
*/
m = SSL_ERROR_WANT_READ;
goto go_again;
}
}
/* normal SSL connection processing path */
#if defined(LWS_WITH_STATS)
if (!wsi->accept_start_us)
wsi->accept_start_us = time_in_microseconds();
#endif
errno = 0;
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
n = SSL_accept(wsi->ssl);
lws_latency(context, wsi,
"SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1);
lwsl_info("SSL_accept says %d\n", n);
if (n == 1)
goto accepted;
m = lws_ssl_get_error(wsi, n);
#if defined(LWS_WITH_MBEDTLS)
if (m == SSL_ERROR_SYSCALL && errno == 11)
m = SSL_ERROR_WANT_READ;
#endif
if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
goto failed;
go_again:
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
goto fail;
}
lwsl_info("SSL_ERROR_WANT_READ\n");
break;
}
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
lwsl_debug("%s: WANT_WRITE\n", __func__);
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
goto fail;
}
break;
}
failed:
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
lws_ssl_get_error_string(m, n, buf, sizeof(buf)));
lws_ssl_elaborate_error();
goto fail;
accepted:
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
#if defined(LWS_WITH_STATS)
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
time_in_microseconds() - wsi->accept_start_us);
wsi->accept_start_us = time_in_microseconds();
#endif
/* adapt our vhost to match the SNI SSL_CTX that was chosen */
vh = context->vhost_list;
while (vh) {
if (!vh->being_destroyed &&
vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
lwsl_info("setting wsi to vh %s\n", vh->name);
wsi->vhost = vh;
break;
}
vh = vh->vhost_next;
}
/* OK, we are accepted... give him some time to negotiate */
lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
context->timeout_secs);
if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW)
wsi->mode = LWSCM_RAW;
else
wsi->mode = LWSCM_HTTP_SERVING;
#if defined(LWS_WITH_HTTP2)
if (lws_h2_configure_if_upgraded(wsi))
goto fail;
#endif
lwsl_debug("accepted new SSL conn\n");
break;
}
return 0;
fail:
return 1;
}
void
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
{
if (vhost->ssl_ctx)
SSL_CTX_free(vhost->ssl_ctx);
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
SSL_CTX_free(vhost->ssl_client_ctx);
}
void
lws_ssl_context_destroy(struct lws_context *context)
{
#if !defined(LWS_WITH_MBEDTLS)
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
// <= 1.0.1f = old api, 1.0.1g+ = new api
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
ERR_remove_state(0);
#else
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
!defined(LIBRESSL_VERSION_NUMBER) && \
!defined(OPENSSL_IS_BORINGSSL)
ERR_remove_thread_state();
#else
ERR_remove_thread_state(NULL);
#endif
#endif
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
SSL_COMP_free_compression_methods();
#endif
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
#endif
#endif
}

153
thirdparty/lws/win32helpers/getopt.c vendored Normal file
View File

@ -0,0 +1,153 @@
/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */
/*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#if 0
static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
#endif
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define __P(x) x
#define _DIAGASSERT(x) assert(x)
#ifdef __weak_alias
__weak_alias(getopt,_getopt);
#endif
int opterr = 1, /* if error message should be printed */
optind = 1, /* index into parent argv vector */
optopt, /* character checked for validity */
optreset; /* reset getopt */
char *optarg; /* argument associated with option */
static char * _progname __P((char *));
int getopt_internal __P((int, char * const *, const char *));
static char *
_progname(nargv0)
char * nargv0;
{
char * tmp;
_DIAGASSERT(nargv0 != NULL);
tmp = strrchr(nargv0, '/');
if (tmp)
tmp++;
else
tmp = nargv0;
return(tmp);
}
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
getopt(nargc, nargv, ostr)
int nargc;
char * const nargv[];
const char *ostr;
{
static char *__progname = 0;
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
__progname = __progname?__progname:_progname(*nargv);
_DIAGASSERT(nargv != NULL);
_DIAGASSERT(ostr != NULL);
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-' /* found "--" */
&& place[1] == '\0') {
++optind;
place = EMSG;
return (-1);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-')
return (-1);
if (!*place)
++optind;
if (opterr && *ostr != ':')
(void)fprintf(stderr,
"%s: illegal option -- %c\n", __progname, optopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
}
else { /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if (*ostr == ':')
return (BADARG);
if (opterr)
(void)fprintf(stderr,
"%s: option requires an argument -- %c\n",
__progname, optopt);
return (BADCH);
}
else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return (optopt); /* dump back option letter */
}

33
thirdparty/lws/win32helpers/getopt.h vendored Normal file
View File

@ -0,0 +1,33 @@
#ifndef __GETOPT_H__
#define __GETOPT_H__
#ifdef __cplusplus
extern "C" {
#endif
extern int opterr; /* if error message should be printed */
extern int optind; /* index into parent argv vector */
extern int optopt; /* character checked for validity */
extern int optreset; /* reset getopt */
extern char *optarg; /* argument associated with option */
struct option
{
const char *name;
int has_arg;
int *flag;
int val;
};
#define no_argument 0
#define required_argument 1
#define optional_argument 2
int getopt(int, char**, char*);
int getopt_long(int, char**, char*, struct option*, int*);
#ifdef __cplusplus
}
#endif
#endif /* __GETOPT_H__ */

View File

@ -0,0 +1,237 @@
/*
* Copyright (c) 1987, 1993, 1994, 1996
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getopt.h"
extern int opterr; /* if error message should be printed */
extern int optind; /* index into parent argv vector */
extern int optopt; /* character checked for validity */
extern int optreset; /* reset getopt */
extern char *optarg; /* argument associated with option */
#define __P(x) x
#define _DIAGASSERT(x) assert(x)
static char * __progname __P((char *));
int getopt_internal __P((int, char * const *, const char *));
static char *
__progname(nargv0)
char * nargv0;
{
char * tmp;
_DIAGASSERT(nargv0 != NULL);
tmp = strrchr(nargv0, '/');
if (tmp)
tmp++;
else
tmp = nargv0;
return(tmp);
}
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
getopt_internal(nargc, nargv, ostr)
int nargc;
char * const *nargv;
const char *ostr;
{
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
_DIAGASSERT(nargv != NULL);
_DIAGASSERT(ostr != NULL);
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-') { /* found "--" */
/* ++optind; */
place = EMSG;
return (-2);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-')
return (-1);
if (!*place)
++optind;
if (opterr && *ostr != ':')
(void)fprintf(stderr,
"%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
} else { /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if ((opterr) && (*ostr != ':'))
(void)fprintf(stderr,
"%s: option requires an argument -- %c\n",
__progname(nargv[0]), optopt);
return (BADARG);
} else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return (optopt); /* dump back option letter */
}
#if 0
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
getopt2(nargc, nargv, ostr)
int nargc;
char * const *nargv;
const char *ostr;
{
int retval;
if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
retval = -1;
++optind;
}
return(retval);
}
#endif
/*
* getopt_long --
* Parse argc/argv argument vector.
*/
int
getopt_long(nargc, nargv, options, long_options, index)
int nargc;
char ** nargv;
char * options;
struct option * long_options;
int * index;
{
int retval;
_DIAGASSERT(nargv != NULL);
_DIAGASSERT(options != NULL);
_DIAGASSERT(long_options != NULL);
/* index may be NULL */
if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
char *current_argv = nargv[optind++] + 2, *has_equal;
int i, current_argv_len, match = -1;
if (*current_argv == '\0') {
return(-1);
}
if ((has_equal = strchr(current_argv, '=')) != NULL) {
current_argv_len = has_equal - current_argv;
has_equal++;
} else
current_argv_len = strlen(current_argv);
for (i = 0; long_options[i].name; i++) {
if (strncmp(current_argv, long_options[i].name, current_argv_len))
continue;
if (strlen(long_options[i].name) == (unsigned)current_argv_len) {
match = i;
break;
}
if (match == -1)
match = i;
}
if (match != -1) {
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (has_equal)
optarg = has_equal;
else
optarg = nargv[optind++];
}
if ((long_options[match].has_arg == required_argument)
&& (optarg == NULL)) {
/*
* Missing argument, leading :
* indicates no error should be generated
*/
if ((opterr) && (*options != ':'))
(void)fprintf(stderr,
"%s: option requires an argument -- %s\n",
__progname(nargv[0]), current_argv);
return (BADARG);
}
} else { /* No matching argument */
if ((opterr) && (*options != ':'))
(void)fprintf(stderr,
"%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
return (BADCH);
}
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
retval = 0;
} else
retval = long_options[match].val;
if (index)
*index = match;
}
return(retval);
}

View File

@ -0,0 +1,36 @@
#include <time.h>
#include <windows.h> //I've omitted context line
#include "gettimeofday.h"
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
unsigned __int64 tmpres = 0;
static int tzflag;
if (NULL != tv) {
GetSystemTimeAsFileTime(&ft);
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;
/*converting file time to unix epoch*/
tmpres /= 10; /*convert into microseconds*/
tmpres -= DELTA_EPOCH_IN_MICROSECS;
tv->tv_sec = (long)(tmpres / 1000000UL);
tv->tv_usec = (long)(tmpres % 1000000UL);
}
if (NULL != tz) {
if (!tzflag) {
_tzset();
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}

View File

@ -0,0 +1,27 @@
#ifndef _GET_TIME_OF_DAY_H
#define _GET_TIME_OF_DAY_H
#include <time.h>
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
#ifdef LWS_MINGW_SUPPORT
#include <winsock2.h>
#endif
#ifndef _TIMEZONE_DEFINED
struct timezone
{
int tz_minuteswest; /* minutes W of Greenwich */
int tz_dsttime; /* type of dst correction */
};
#endif
int gettimeofday(struct timeval *tv, struct timezone *tz);
#endif