Add libwebsockets as thirdparty library.
License is LGPLv2.1 + static linking exception, version is 2.4.1
This commit is contained in:
parent
e7cf2b2e77
commit
fa33e0f62d
45 changed files with 35743 additions and 0 deletions
17
thirdparty/README.md
vendored
17
thirdparty/README.md
vendored
|
@ -217,6 +217,23 @@ changes to ensure they build for Javascript/HTML5. Those
|
||||||
changes are marked with `// -- GODOT --` comments.
|
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
|
## minizip
|
||||||
|
|
||||||
- Upstream: http://www.zlib.net
|
- Upstream: http://www.zlib.net
|
||||||
|
|
555
thirdparty/lws/LICENSE.txt
vendored
Normal file
555
thirdparty/lws/LICENSE.txt
vendored
Normal 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
90
thirdparty/lws/alloc.c
vendored
Normal 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
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
598
thirdparty/lws/client/client-parser.c
vendored
Normal 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
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
625
thirdparty/lws/client/ssl-client.c
vendored
Normal 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
1628
thirdparty/lws/context.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
473
thirdparty/lws/ext/extension-permessage-deflate.c
vendored
Normal file
473
thirdparty/lws/ext/extension-permessage-deflate.c
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
|
41
thirdparty/lws/ext/extension-permessage-deflate.h
vendored
Normal file
41
thirdparty/lws/ext/extension-permessage-deflate.h
vendored
Normal 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
344
thirdparty/lws/ext/extension.c
vendored
Normal 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
280
thirdparty/lws/handshake.c
vendored
Normal 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
355
thirdparty/lws/header.c
vendored
Normal 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
104
thirdparty/lws/lextable-strings.h
vendored
Normal 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
805
thirdparty/lws/lextable.h
vendored
Normal 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
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
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
166
thirdparty/lws/lws_config.h
vendored
Normal 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
143
thirdparty/lws/lws_config_private.h
vendored
Normal 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
272
thirdparty/lws/minilex.c
vendored
Normal 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
206
thirdparty/lws/misc/base64-decode.c
vendored
Normal 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
270
thirdparty/lws/misc/getifaddrs.c
vendored
Normal 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
80
thirdparty/lws/misc/getifaddrs.h
vendored
Normal 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
709
thirdparty/lws/misc/lejp.c
vendored
Normal 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
232
thirdparty/lws/misc/lejp.h
vendored
Normal 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
300
thirdparty/lws/misc/sha-1.c
vendored
Normal 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
877
thirdparty/lws/output.c
vendored
Normal 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
850
thirdparty/lws/plat/lws-plat-unix.c
vendored
Normal 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
745
thirdparty/lws/plat/lws-plat-win.c
vendored
Normal 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
557
thirdparty/lws/pollfd.c
vendored
Normal 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
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
669
thirdparty/lws/server/fops-zip.c
vendored
Normal 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
929
thirdparty/lws/server/lejp-conf.c
vendored
Normal 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
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
214
thirdparty/lws/server/ranges.c
vendored
Normal 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
360
thirdparty/lws/server/server-handshake.c
vendored
Normal 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
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
477
thirdparty/lws/server/ssl-server.c
vendored
Normal 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
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
976
thirdparty/lws/ssl.c
vendored
Normal 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
153
thirdparty/lws/win32helpers/getopt.c
vendored
Normal 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
33
thirdparty/lws/win32helpers/getopt.h
vendored
Normal 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__ */
|
237
thirdparty/lws/win32helpers/getopt_long.c
vendored
Normal file
237
thirdparty/lws/win32helpers/getopt_long.c
vendored
Normal 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);
|
||||||
|
}
|
36
thirdparty/lws/win32helpers/gettimeofday.c
vendored
Normal file
36
thirdparty/lws/win32helpers/gettimeofday.c
vendored
Normal 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;
|
||||||
|
}
|
27
thirdparty/lws/win32helpers/gettimeofday.h
vendored
Normal file
27
thirdparty/lws/win32helpers/gettimeofday.h
vendored
Normal 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
|
Loading…
Reference in a new issue