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