Compare commits

...

129 Commits

Author SHA1 Message Date
Sergey Kandaurov 78d1ab5a2c SSL: support for compressed server certificates with BoringSSL.
BoringSSL/AWS-LC provide two callbacks for each compression algorithm,
which may be used to compress and decompress certificates in runtime.
This change implements compression support with zlib, as enabled with
the ssl_certificate_compression directive.  Compressed certificates
are stored in certificate exdata and reused in subsequent connections.

Notably, AWS-LC saves an X509 pointer in SSL connection, which allows
to use it from SSL_get_certificate() for caching purpose.  In contrast,
BoringSSL reconstructs X509 on-the-fly, though given that it doesn't
support multiple certificates, always replacing previously configured
certificates, we use the last configured one from ssl->certs, instead.
2025-10-08 19:56:41 +04:00
Sergey Kandaurov 25b03d6500 SSL: disabled using certificate compression with OCSP stapling.
OCSP response in TLSv1.3 is sent in the Certificate message.  This
is incompatible with pre-compression of the configured certificates.
2025-10-08 19:56:41 +04:00
Sergey Kandaurov f5a989cda2 Updated OpenSSL and PCRE used for win32 builds. 2025-10-08 19:56:41 +04:00
Sergey Kandaurov 3494f25c3e Version bump. 2025-10-08 19:56:41 +04:00
Sergey Kandaurov 7f71abdd14 nginx-1.29.2-RELEASE 2025-10-07 21:04:07 +04:00
Sergey Kandaurov 9d5cdc616c Fixed a typo. 2025-10-07 21:04:07 +04:00
Sergey Kandaurov a144d828cb SSL: fixed "key values mismatch" with object cache inheritance.
In rare cases, it was possible to get into this error state on reload
with improperly updated file timestamps for certificate and key pairs.

The fix is to retry on X509_R_KEY_VALUES_MISMATCH, similar to 5d5d9adcc.
Additionally, loading SSL certificate is updated to avoid certificates
discarded on retry to appear in ssl->certs and in extra chain.
2025-10-06 12:56:42 +04:00
Maryna-f5 c2a266fa78 Added F5 CLA workflow. 2025-09-30 11:44:52 -07:00
Sergey Kandaurov 6f81314a07 Mail: xtext encoding (RFC 3461) in XCLIENT LOGIN.
The XCLIENT command uses xtext encoding for attribute values,
as specified in https://www.postfix.org/XCLIENT_README.html.

Reported by Igor Morgenstern of Aisle Research.
2025-09-26 17:04:20 +04:00
Sergey Kandaurov 8255bd29ac Upstream: overflow detection in Cache-Control delta-seconds.
Overflowing calculations are now aligned to the greatest positive integer
as specified in RFC 9111, Section 1.2.2.
2025-09-26 16:50:13 +04:00
Sergey Kandaurov 93ff1ee12c SSL: AWS-LC compatibility. 2025-09-25 19:28:36 +04:00
Sergey Kandaurov af436c58ca QUIC: a new macro to differentiate BoringSSL specific EVP API. 2025-09-25 19:28:36 +04:00
Sergey Kandaurov 4c9ae11dff QUIC: localized OpenSSL headers used for QUIC protection. 2025-09-25 19:28:36 +04:00
Sergey Kandaurov 7f9ced0ce0 SNI: support for early ClientHello callback with BoringSSL.
This brings feature parity with OpenSSL after the previous change,
making it possible to set SSL protocols per virtual server.
2025-09-25 19:25:08 +04:00
Sergey Kandaurov 0373fe5d98 SNI: using the ClientHello callback.
The change introduces an SNI based virtual server selection during
early ClientHello processing.  The callback is available since
OpenSSL 1.1.1; for older OpenSSL versions, the previous behaviour
is kept.

Using the ClientHello callback sets a reasonable processing order
for the "server_name" TLS extension.  Notably, session resumption
decision now happens after applying server configuration chosen by
SNI, useful with enabled verification of client certificates, which
brings consistency with BoringSSL behaviour.  The change supersedes
and reverts a fix made in 46b9f5d38 for TLSv1.3 resumed sessions.

In addition, since the callback is invoked prior to the protocol
version negotiation, this makes it possible to set "ssl_protocols"
on a per-virtual server basis.

To keep the $ssl_server_name variable working with TLSv1.2 resumed
sessions, as previously fixed in fd97b2a80, a limited server name
callback is preserved in order to acknowledge the extension.

Note that to allow third-party modules to properly chain the call to
ngx_ssl_client_hello_callback(), the servername callback function is
passed through exdata.
2025-09-25 19:25:08 +04:00
willmafh bc71625dcc Fixed inaccurate index directive error report. 2025-09-18 18:16:22 +04:00
Sergey Kandaurov 417c87b78d Updated link to xslscript. 2025-09-15 22:13:27 +04:00
Sergey Kandaurov eb5ebbbed7 QUIC: fixed ssl_reject_handshake error handling.
This was broken in 7468a10b6 (1.29.0), resulting in a missing diagnostics
and SSL error queue not cleared for SSL handshakes rejected by SNI, seen
as "ignoring stale global SSL error" alerts, for instance, when doing SSL
shutdown of a long standing connection after rejecting another one by SNI.

The fix is to move the qc->error check after c->ssl->handshake_rejected is
handled first, to make the error queue cleared.  Although not practicably
visible as needed, this is accompanied by clearing the error queue under
the qc->error case as well, to be on the safe side.

As an implementation note, due to the way of handling invalid transport
parameters for OpenSSL 3.5 and above, which leaves a passed pointer not
advanced on error, SSL_get_error() may return either SSL_ERROR_WANT_READ
or SSL_ERROR_WANT_WRITE depending on a library.  To cope with that, both
qc->error and c->ssl->handshake_rejected checks were moved out of
"sslerr != SSL_ERROR_WANT_READ".

Also, this reconstructs a missing "SSL_do_handshake() failed" diagnostics
for the qc->error case, replacing using ngx_ssl_connection_error() with
ngx_connection_error().  It is made this way to avoid logging at the crit
log level because qc->error set is expected to have an empty error queue.

Reported and tested by Vladimir Homutov.
2025-09-12 17:57:48 +04:00
Mohamed Karrab 446ce033e5 Removed legacy charset directive from default config example.
The example configuration previously specified 'charset koi8-r',
which is a legacy Cyrillic encoding.  As koi8-r is rarely used today
and modern browsers handle UTF-8 by default, specifying the charset
explicitly is unnecessary.  Removing the directive keeps the example
configuration concise and aligned with current best practices.
2025-08-19 15:47:51 +04:00
Sergey Kandaurov 1a82df8cca Added a previously missed changes entry in 1.29.1 relnotes. 2025-08-13 21:21:40 +04:00
Sergey Kandaurov 36d40e5610 Version bump. 2025-08-13 21:21:40 +04:00
Sergey Kandaurov 0024724f2f nginx-1.29.1-RELEASE 2025-08-13 18:33:41 +04:00
Sergey Kandaurov cc1c07ca33 Updated OpenSSL used for win32 builds. 2025-08-13 18:33:41 +04:00
Sergey Kandaurov 239e10793a Mail: logging upstream to the error log with "smtp_auth none;".
Previously, it was never logged because of missing login.
2025-08-13 18:20:34 +04:00
Sergey Kandaurov 9c02c84a74 Mail: reset stale auth credentials with "smtp_auth none;".
They might be reused in a session if an SMTP client proceeded
unauthenticated after previous invalid authentication attempts.
This could confuse an authentication server when passing stale
credentials along with "Auth-Method: none".

The condition to send the "Auth-Salt" header is similarly refined.
2025-08-13 18:20:34 +04:00
Sergey Kandaurov 765642b86e Mail: improved error handling in plain/login/cram-md5 auth methods.
Previously, login and password storage could be left in inconsistent
state in a session after decoding errors.
2025-08-13 18:20:34 +04:00
Sergey Kandaurov 034f15bbc2 Auth basic: fixed file descriptor leak on memory allocation error.
Found by Coverity (CID 1662016).
2025-08-11 20:57:47 +04:00
Sergey Kandaurov 251444fcf4 SSL: support for compressed server certificates with OpenSSL.
The ssl_certificate_compression directive allows to send compressed
server certificates.  In OpenSSL, they are pre-compressed on startup.
To simplify configuration, the SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
option is automatically cleared if certificates were pre-compressed.

SSL_CTX_compress_certs() may return an error in legitimate cases,
e.g., when none of compression algorithms is available or if the
resulting compressed size is larger than the original one, thus it
is silently ignored.

Certificate compression is supported in Chrome with brotli only,
in Safari with zlib only, and in Firefox with all listed algorithms.
It is supported since Ubuntu 24.10, which has OpenSSL with enabled
zlib and zstd support.

The actual list of algorithms supported in OpenSSL depends on how
the library was configured; it can be brotli, zlib, zstd as listed
in RFC 8879.
2025-08-03 19:15:16 +04:00
Sergey Kandaurov ed99269eed SSL: disabled certificate compression by default with OpenSSL.
Certificate compression is supported since OpenSSL 3.2, it is enabled
automatically as negotiated in a TLSv1.3 handshake.

Using certificate compression and decompression in runtime may be
suboptimal in terms of CPU and memory consumption in certain typical
scenarios, hence it is disabled by default on both server and client
sides.  It can be enabled with ssl_conf_command and similar directives
in upstream as appropriate, for example:

    ssl_conf_command Options RxCertificateCompression;
    ssl_conf_command Options TxCertificateCompression;

Compressing server certificates requires additional support, this is
addressed separately.
2025-08-03 19:15:16 +04:00
Sergey Kandaurov f4005126d7 Updated ngx_http_process_multi_header_lines() comments.
Missed in fcf4331a0.
2025-08-03 10:07:07 +04:00
Sergey Kandaurov 372659114e HTTP/3: improved invalid ":authority" error message. 2025-08-03 10:07:07 +04:00
Sergey Kandaurov 4d857aaf43 Made ngx_http_process_request_header() static again.
The function contains mostly HTTP/1.x specific request processing,
which has no use in other protocols.  After the previous change in
HTTP/2, it can now be hidden.

This is an API change.
2025-08-03 10:07:07 +04:00
Sergey Kandaurov ede5623b15 HTTP/2: fixed handling of the ":authority" header.
Previously, it misused the Host header processing resulting in
400 (Bad Request) errors for a valid request that contains both
":authority" and Host headers with the same value, treating it
after 37984f0be as if client sent more than one Host header.
Such an overly strict handling violates RFC 9113.

The fix is to process ":authority" as a distinct header, similarly
to processing an authority component in the HTTP/1.x request line.
This allows to disambiguate and compare Host and ":authority"
values after all headers were processed.

With this change, the ngx_http_process_request_header() function
can no longer be used here, certain parts were inlined similar to
the HTTP/3 module.

To provide compatibility for misconfigurations that use $http_host
to return the value of the ":authority" header, the Host header,
if missing, is now reconstructed from ":authority".
2025-08-03 10:07:07 +04:00
Sergey Kandaurov a238bb3d22 HTTP/2: factored out constructing the Host header.
No functional changes.
2025-08-03 10:07:07 +04:00
Roman Arutyunyan 50932c3c6c HTTP/2: fixed flushing early hints over SSL.
Previously, when using HTTP/2 over SSL, an early hints HEADERS frame was
queued in SSL buffer, and might not be immediately flushed.  This resulted
in a delay of early hints delivery until the main response was sent.

The fix is to set the flush flag for the early hints HEADERS frame buffer.
2025-07-28 21:06:48 +04:00
Roman Arutyunyan 4da7711082 HTTP/3: fixed handling of :authority and Host with port.
RFC 9114, Section 4.3.1. specifies a restriction for :authority and Host
coexistence in an HTTP/3 request:

: If both fields are present, they MUST contain the same value.

Previously, this restriction was correctly enforced only for portless
values.  When Host contained a port, the request failed as if :authority
and Host were different, regardless of :authority presence.

This happens because the value of r->headers_in.server used for :authority
has port stripped.  The fix is to use r->host_start / r->host_end instead.
2025-07-24 20:15:55 +04:00
Sergey Kandaurov 3739fe94d1 HTTP/3: fixed potential type overflow in string literal parser.
This might happen for Huffman encoded string literals as the result
of length expansion.  Notably, the maximum length of string literals
is already limited with the "large_client_header_buffers" directive,
so this was only possible with nonsensically large configured limits.
2025-07-23 17:24:43 +04:00
Sergey Kandaurov c52c5698cd Events: compatibility with NetBSD 10.0 in kqueue.
The kevent udata field was changed from intptr_t to "void *",
similar to other BSDs and Darwin.

The NGX_KQUEUE_UDATA_T macro is adjusted to reflect that change,
fixing -Werror=int-conversion errors.
2025-07-11 16:25:51 +04:00
Sergey Kandaurov 3f5f8a7f51 Configure: set NGX_KQUEUE_UDATA_T at compile time.
The NGX_KQUEUE_UDATA_T macro is used to compensate the incompatible
kqueue() API in NetBSD, it doesn't really belong to feature tests.

The change limits the macro visibility to the kqueue event module.
Moving from autotests also simplifies testing a particular NetBSD
version as seen in a subsequent change.
2025-07-11 16:25:51 +04:00
Sergey Kandaurov 0daaba5c54 Events: fixed -Wzero-as-null-pointer-constant warnings in kqueue.
The kevent udata field is special in that we maintain compatibility
with NetBSD versions that predate using the "void *" type.

The fix is to cast to intermediate uintptr_t that is casted back to
"void *" where appropriate.
2025-07-11 16:25:51 +04:00
Sergey Kandaurov a5ca38f303 SSL: fixed testing OPENSSL_VERSION_NUMBER for OpenSSL 3.0+.
Prior to OpenSSL 3.0, OPENSSL_VERSION_NUMBER used the following format:

MNNFFPPS: major minor fix patch status

Where the status nibble (S) has 0+ for development and f for release.

The format was changed in OpenSSL 3.0.0, where it is always zero:

MNN00PP0: major minor patch
2025-07-10 19:00:45 +04:00
Sergey Kandaurov a5d60c30d3 SSL: SSL_group_to_name() compatibility macro.
No functional changes.
2025-07-10 19:00:45 +04:00
Sergey Kandaurov 0bb7489cb2 QUIC: adjusted OpenSSL 3.5 QUIC API feature test.
A bug with the "quic_transport_parameters" extension and SNI described
in cedb855d7 is now fixed in the OpenSSL 3.5.1 release, as requested
in https://github.com/openssl/openssl/pull/27706.
2025-07-03 22:50:25 +04:00
Sergey Kandaurov d1843e1d9b Win32: fixed PCRE license for nginx/Windows zip. 2025-06-25 14:19:13 +04:00
Sergey Kandaurov 279fe352cb Version bump. 2025-06-25 14:19:13 +04:00
Sergey Kandaurov 235f409907 nginx-1.29.0-RELEASE 2025-06-24 21:22:41 +04:00
Sergey Kandaurov 1263d6bec3 Updated OpenSSL and PCRE used for win32 builds. 2025-06-24 21:22:41 +04:00
Sergey Kandaurov b997be14f5 Win32: skip OpenSSL dependency generation to conserve time.
Disabling the build dependency feature is safe assuming that
nginx/Windows release zip is always built from a clean tree.
This allows to speed up total build time by around 40%.

As it may not be suitable in general, the option resides here
and not in configure.
2025-06-24 21:22:41 +04:00
Sergey Kandaurov cedb855d75 QUIC: disabled OpenSSL 3.5 QUIC API support by default.
In OpenSSL 3.5.0, the "quic_transport_parameters" extension set
internally by the QUIC API is cleared on the SSL context switch,
which disables sending QUIC transport parameters if switching to
a different server block on SNI.  See the initial report in [1].

This is fixed post OpenSSL 3.5.0 [2].  The fix is anticipated in
OpenSSL 3.5.1, which has not been released yet.  When building
with OpenSSL 3.5, OpenSSL compat layer is now used by default.
The OpenSSL 3.5 QUIC API support can be switched back using
--with-cc-opt='-DNGX_QUIC_OPENSSL_API=1'.

[1] https://github.com/nginx/nginx/issues/711
[2] https://github.com/openssl/openssl/commit/45bd3c3798
2025-06-23 22:35:09 +04:00
Sergey Kandaurov cdf7a9c6cb Upstream: fixed reinit request with gRPC and Early Hints.
The gRPC module context has connection specific state, which can be lost
after request reinitialization when it comes to processing early hints.

The fix is to do only a portion of u->reinit_request() implementation
required after processing early hints, now inlined in modules.

Now NGX_HTTP_UPSTREAM_EARLY_HINTS is returned from u->process_header()
for early hints.  When reading a cached response, this code is mapped
to NGX_HTTP_UPSTREAM_INVALID_HEADER to indicate invalid header format.
2025-06-23 20:12:21 +04:00
Andrew Clayton 4eaecc5e8a Use NULL instead of 0 for null pointer constant.
There were a few random places where 0 was being used as a null pointer
constant.

We have a NULL macro for this very purpose, use it.

There is also some interest in actually deprecating the use of 0 as a
null pointer constant in C.

This was found with -Wzero-as-null-pointer-constant which was enabled
for C in GCC 15 (not enabled with Wall or Wextra... yet).

Link: <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117059>
2025-06-21 10:36:45 +04:00
Andrew Clayton c370ac8a51 Use NGX_CONF_OK in some function return checks.
The functions ngx_http_merge_types() & ngx_conf_merge_path_value()
return either NGX_CONF_OK aka NULL aka ((void *)0) (probably) or
NGX_CONF_ERROR aka ((void *)-1).

They don't return an integer constant which is what NGX_OK aka (0) is.

Lets use the right thing in the function return check.

This was found with -Wzero-as-null-pointer-constant which was enabled
for C in GCC 15 (not enabled with Wall or Wextra... yet).

Link: <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117059>
2025-06-21 10:36:45 +04:00
Sergey Kandaurov ba917b1360 HTTP/3: indexed field line encoding for "103 Early Hints". 2025-06-21 10:36:28 +04:00
Roman Arutyunyan 662c1dd2a9 Upstream: early hints support.
The change implements processing upstream early hints response in
ngx_http_proxy_module and ngx_http_grpc_module.  A new directive
"early_hints" enables sending early hints to the client.  By default,
sending early hints is disabled.

Example:

    map $http_sec_fetch_mode $early_hints {
        navigate $http2$http3;
    }

    early_hints $early_hints;

    proxy_pass http://example.com;
2025-06-19 10:19:57 +04:00
Roman Arutyunyan ea001feb10 HTTP/2: added function declaration. 2025-06-19 10:19:57 +04:00
Sergey Kandaurov 5b8a5c08ce Core: added support for TCP keepalive parameters on macOS.
The support first appeared in OS X Mavericks 10.9 and documented since
OS X Yosemite 10.10.

It has a subtle implementation difference from other operating systems
in that the TCP_KEEPALIVE socket option (used in place of TCP_KEEPIDLE)
isn't inherited from a listening socket to an accepted socket.

An apparent reason for this behaviour is that it might be preserved for
the sake of backward compatibility.  The TCP_KEEPALIVE socket option is
not inherited since appearance in OS X Panther 10.3, which long predates
two other TCP_KEEPINTVL and TCP_KEEPCNT socket options.

Thanks to Andy Pan for initial work.
2025-05-27 01:59:02 +04:00
Aleksei Bavshin 3d5889a3ee SSL: disabled UI console prompts from worker processes.
Certain providers may attempt to reload the key on the first use after a
fork.  Such attempt would require re-prompting the pin, and this time we
are not able to pass the password callback.

While it is addressable with configuration for a specific provider, it would
be prudent to ensure that no such prompts could block worker processes by
setting the default UI method.

UI_null() first appeared in 1.1.1 along with the OSSL_STORE, so it is safe
to assume the same set of guards.
2025-05-26 06:56:18 -07:00
Aleksei Bavshin 0fdbfc1ff4 SSL: support loading keys via OSSL_STORE.
A new "store:..." prefix for the "ssl_certificate_key" directive allows
loading keys via the OSSL_STORE API.

The change is required to support hardware backed keys in OpenSSL 3.x using
the new "provider(7ossl)" modules, such as "pkcs11-provider".  While the
engine API is present in 3.x, some operating systems (notably, RHEL10)
have already disabled it in their builds of OpenSSL.

Related: https://trac.nginx.org/nginx/ticket/2449
2025-05-26 06:56:18 -07:00
Sergey Kandaurov 6a134dfd48 QUIC: using QUIC API introduced in OpenSSL 3.5.
Similarly to the QUIC API originated in BoringSSL, this API allows
to register custom TLS callbacks for an external QUIC implementation.
See the SSL_set_quic_tls_cbs manual page for details.

Due to a different approach used in OpenSSL 3.5, handling of CRYPTO
frames was streamlined to always write an incoming CRYPTO buffer to
the crypto context.  Using SSL_provide_quic_data(), this results in
transient allocation of chain links and buffers for CRYPTO frames
received in order.  Testing didn't reveal performance degradation of
QUIC handshakes, https://github.com/nginx/nginx/pull/646 provides
specific results.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov 1d4d2f2c96 QUIC: better approach for premature handshake completion.
Using SSL_in_init() to inspect a handshake state was replaced with
SSL_is_init_finished().  This represents a more complete fix to the
BoringSSL issue addressed in 22671b37e.

This provides awareness of the early data handshake state when using
OpenSSL 3.5 TLS callbacks in 0-RTT enabled configurations, which, in
particular, is used to avoid premature completion of the initial TLS
handshake, before required client handshake messages are received.

This is a non-functional change when using BoringSSL.  It supersedes
testing non-positive SSL_do_handshake() results in all supported SSL
libraries, hence simplified.

In preparation for using OpenSSL 3.5 TLS callbacks.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov bcb9d3fd2c QUIC: ssl_encryption_level_t abstraction layer.
Encryption level values are decoupled from ssl_encryption_level_t,
which is now limited to BoringSSL QUIC callbacks, with mappings
provided.  Although the values match, this provides a technically
safe approach, in particular, to access protection level sized arrays.

In preparation for using OpenSSL 3.5 TLS callbacks.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov 9857578f15 QUIC: factored out SSL_provide_quic_data() to the helper function.
It is now called from ngx_quic_handle_crypto_frame(), prior to proceeding
with the handshake.  With this logic removed, the handshake function is
renamed to ngx_quic_handshake() to better match ngx_ssl_handshake().
2025-05-23 15:00:47 +04:00
Sergey Kandaurov e561f7dbcf QUIC: defined SSL API macros in a single place.
All definitions now set in ngx_event_quic.h, this includes moving
NGX_QUIC_OPENSSL_COMPAT from autotests to compile time.  Further,
to improve code readability, a new NGX_QUIC_QUICTLS_API macro is
used for QuicTLS that provides old BoringSSL QUIC API.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov 54e6b7cfee QUIC: logging missing mandatory TLS extensions only once.
Previously, they might be logged on every add_handshake_data
callback invocation when using OpenSSL compat layer and processing
coalesced handshake messages.

Further, the ALPN error message is adjusted to signal the missing
extension.  Possible reasons were previously narrowed down with
ebb6f7d65 changes in the ALPN callback that is invoked earlier in
the handshake.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov 5d7fd4a7e3 QUIC: reset qc->error to zero again.
Following the previous change that removed posting a close event
in OpenSSL compat layer, now ngx_quic_close_connection() is always
called on error path with either NGX_ERROR or qc->error set.

This allows to remove a special value -1 served as a missing error,
which simplifies the code.  Partially reverts d3fb12d77.

Also, this improves handling of the draining connection state, which
consists of posting a close event with NGX_OK and no qc->error set,
where it was previously converted to NGX_QUIC_ERR_INTERNAL_ERROR.
Notably, this is rather a cosmetic fix, because drained connections
do not send any packets including CONNECTION_CLOSE, and qc->error
is not otherwise used.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov 7468a10b62 QUIC: adjusted handling of callback errors.
Changed handshake callbacks to always return success.  This allows to avoid
logging SSL_do_handshake() errors with empty or cryptic "internal error"
OpenSSL error messages at the inappropriate "crit" log level.

Further, connections with failed callbacks are closed now right away when
using OpenSSL compat layer.  This change supersedes and reverts c37fdcdd1,
with the conditions to check callbacks invocation kept to slightly improve
code readability of control flow; they are optimized out in the resulting
assembly code.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov 47f96993f6 QUIC: logging of SSL library errors.
Logging level for such errors, which should not normally happen,
is changed to NGX_LOG_ALERT, and ngx_log_error() is replaced with
ngx_ssl_error() for consistency with the rest of the code.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov ef9cd3214f QUIC: logging level of handshake errors.
Various errors reported by SSL_do_handshake() are now logged at the
"info" or "crit" level, akin to handshakes on regular TCP connections.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov aa43385ffa QUIC: removed ALPN feature test.
ALPN support is present in all libraries that have QUIC support,
it is safe to compile it unconditionally.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov 39b1e3fe9d QUIC: removed excessive casts for ngx_ssl_get_connection().
They were blindly copied from ngx_ssl_info_callback(), where
the ngx_ssl_conn_t pointer is passed with const qualifier.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov 335993154c QUIC: removed level field from ngx_quic_compat_record_t.
It was made unused in d15f8f2 after introducing reusable crypto contexts.
2025-05-23 15:00:47 +04:00
Sergey Kandaurov f3542500b6 QUIC: do not block ACKs by congestion control.
Previously, it was not possible to send acknowledgments if the
congestion window was limited or temporarily exceeded, such as
after sending a large response or MTU probe.  If ACKs were not
received from the peer for some reason to update the in-flight
bytes counter below the congestion window, this might result in
a stalled connection.

The fix is to send ACKs regardless of congestion control.  This
meets RFC 9002, Section 7:
: Similar to TCP, packets containing only ACK frames do not count
: toward bytes in flight and are not congestion controlled.

This is a simplified implementation to send ACK frames from the
head of the queue.  This was made possible after 6f5f17358.

Reported in trac ticket #2621 and subsequently by Vladimir Homutov:
https://mailman.nginx.org/pipermail/nginx-devel/2025-April/ZKBAWRJVQXSZ2ISG3YJAF3EWMDRDHCMO.html
2025-04-29 19:53:41 +04:00
Sergey Kandaurov adda704158 SSL: fixed build with OPENSSL_NO_DH. 2025-04-25 14:56:31 +04:00
Sergey Kandaurov 4f8bc0b282 SSL: fixed build with OPENSSL_NO_DEPRECATED. 2025-04-25 14:56:31 +04:00
nandsky 9785db9bd5 QUIC: fixed a typo. 2025-04-23 11:53:51 +04:00
Aleksei Bavshin 020b1db7eb Win32: added detection of ARM64 target.
This extends the target selection implemented in dad6ec3aa6 to support
Windows ARM64 platforms.  OpenSSL support for VC-WIN64-ARM target first
appeared in 1.1.1 and is present in all currently supported (3.x)
branches.

As a side effect, ARM64 Windows builds will get 16-byte alignment along
with the rest of non-x86 platforms.  This is safe, as malloc on 64-bit
Windows guarantees the fundamental alignment of allocations, 16 bytes.
2025-04-18 12:57:26 -07:00
Aleksei Bavshin b9d0ba6677 Core: improved NGX_ALIGNMENT detection on some x86_64 platforms.
Previously, the default pool alignment used sizeof(unsigned long), with
the expectation that this would match to a platform word size.  Certain
64-bit platforms prove this assumption wrong by keeping the 32-bit long
type, which is fully compliant with the C standard.

This introduces a possibility of suboptimal misaligned access to the
data allocated with ngx_palloc() on the affected platforms, which is
addressed here by changing the default NGX_ALIGNMENT to a pointer size.

As we override the detection in auto/os/conf for all the machine types
except x86, and Unix-like 64-bit systems prefer the 64-bit long, the
impact of the change should be limited to Win64 x64.
2025-04-18 12:57:26 -07:00
Roman Arutyunyan 0f9f43b79e HTTP/3: fixed NGX_HTTP_V3_VARLEN_INT_LEN value.
After fixing ngx_http_v3_encode_varlen_int() in 400eb1b628,
NGX_HTTP_V3_VARLEN_INT_LEN retained the old value of 4, which is
insufficient for the values over 1073741823 (1G - 1).

The NGX_HTTP_V3_VARLEN_INT_LEN macro is used in ngx_http_v3_uni.c to
format stream and frame types.  Old buffer size is enough for formatting
this data.  Also, the macro is used in ngx_http_v3_filter_module.c to
format output chunks and trailers.  Considering output_buffers and
proxy_buffer_size are below 1G in all realistic scenarios, the old buffer
size is enough here as well.
2025-04-18 15:28:00 +04:00
Roman Arutyunyan 444954abac Fixed -Wunterminated-string-initialization with gcc15. 2025-04-17 19:12:59 +04:00
Roman Arutyunyan 04813dac86 QUIC: lowered log level for unsupported transport parameters. 2025-04-17 12:51:17 +04:00
Roman Arutyunyan 0626e60a75 Version bump. 2025-04-16 18:55:19 +04:00
Sergey Kandaurov 6ac8b69f06 nginx-1.27.5-RELEASE 2025-04-16 16:01:11 +04:00
Roman Arutyunyan aa49a416b8 QUIC: dynamic packet threshold.
RFC 9002, Section 6.1.1 defines packet reordering threshold as 3.  Testing
shows that such low value leads to spurious packet losses followed by
congestion window collapse.  The change implements dynamic packet threshold
detection based on in-flight packet range.  Packet threshold is defined
as half the number of in-flight packets, with mininum value of 3.

Also, renamed ngx_quic_lost_threshold() to ngx_quic_time_threshold()
for better compliance with RFC 9002 terms.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan 2fb32ff24d QUIC: optimized connection frame threshold.
Previosly the threshold was hardcoded at 10000.  This value is too low for
high BDP networks.  For example, if all frames are STREAM frames, and MTU
is 1500, the upper limit for congestion window would be roughly 15M
(10000 * 1500).  With 100ms RTT it's just a 1.2Gbps network (15M * 10 * 8).
In reality, the limit is even lower because of other frame types.  Also,
the number of frames that could be used simultaneously depends on the total
amount of data buffered in all server streams, and client flow control.

The change sets frame threshold based on max concurrent streams and stream
buffer size, the product of which is the maximum number of in-flight stream
data in all server streams at any moment.  The value is divided by 2000 to
account for a typical MTU 1500 and the fact that not all frames are STREAM
frames.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan f9a7e7cc11 QUIC: CUBIC congestion control. 2025-04-15 19:01:36 +04:00
Roman Arutyunyan a40cc70023 QUIC: ignore congestion control when sending MTU probes.
If connection is network-limited, MTU probes have little chance of being
sent since congestion window is almost always full.  As a result, PMTUD
may not be able to reach the real MTU and the connection may operate with
a reduced MTU.  The solution is to ignore the congestion window.  This may
lead to a temporary increase in in-flight count beyond congestion window.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan 6bf13e9d57 QUIC: do not shrink congestion window after losing an MTU probe.
As per RFC 9000, Section 14.4:

    Loss of a QUIC packet that is carried in a PMTU probe is therefore
    not a reliable indication of congestion and SHOULD NOT trigger a
    congestion control reaction.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan cd5e4fa144 QUIC: do not increase underutilized congestion window.
As per RFC 9002, Section 7.8, congestion window should not be increased
when it's underutilized.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan 04c65ccd9a QUIC: all-levels commit and revert functions.
Previously, these functions operated on a per-level basis.  This however
resulted in excessive logging of in_flight and will also led to extra
work detecting underutilized congestion window in the followup patches.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan 1e883a40db QUIC: ngx_msec_t overflow protection.
On some systems the value of ngx_current_msec is derived from monotonic
clock, for which the following is defined by POSIX:

   For this clock, the value returned by clock_gettime() represents
   the amount of time (in seconds and nanoseconds) since an unspecified
   point in the past.

As as result, overflow protection is needed when comparing two ngx_msec_t.
The change adds such protection to the ngx_quic_detect_lost() function.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan 38236bf74f QUIC: prevent spurious congestion control recovery mode.
Since recovery_start field was initialized with ngx_current_msec, all
congestion events that happened within the same millisecond or cycle
iteration, were treated as in recovery mode.

Also, when handling persistent congestion, initializing recovery_start
with ngx_current_msec resulted in treating all sent packets as in recovery
mode, which violates RFC 9002, see example in Appendix B.8.

While here, also fixed recovery_start wrap protection.  Previously it used
2 * max_idle_timeout time frame for all sent frames, which is not a
reliable protection since max_idle_timeout is unrelated to congestion
control.  Now recovery_start <= now condition is enforced.  Note that
recovery_start wrap is highly unlikely and can only occur on a
32-bit system if there are no congestion events for 24 days.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan 53e7e9eb54 QUIC: use path MTU in congestion window computations.
As per RFC 9002, Section B.2, max_datagram_size used in congestion window
computations should be based on path MTU.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan 3a97111adf HTTP/3: graceful shutdown on keepalive timeout expiration.
Previously, the expiration caused QUIC connection finalization even if
there are application-terminated streams finishing sending data.  Such
finalization terminated these streams.

An easy way to trigger this is to request a large file from HTTP/3 over
a small MTU.  In this case keepalive timeout expiration may abruptly
terminate the request stream.
2025-04-15 19:01:36 +04:00
Roman Arutyunyan 2b8b70068a QUIC: graph-friendly congestion control logging.
Improved logging for simpler data extraction for plotting congestion
window graphs.  In particular, added current milliseconds number from
ngx_current_msec.

While here, simplified logging text and removed irrelevant data.
2025-04-15 19:01:36 +04:00
Sergey Kandaurov b6e7eb0f57 SSL: external groups support in $ssl_curve and $ssl_curves.
Starting with OpenSSL 3.0, groups may be added externally with pluggable
KEM providers.  Using SSL_get_negotiated_group(), which makes lookup in a
static table with known groups, doesn't allow to list such groups by names
leaving them in hex.  Adding X25519MLKEM768 to the default group list in
OpenSSL 3.5 made this problem more visible.  SSL_get0_group_name() and,
apparently, SSL_group_to_name() allow to resolve such provider-implemented
groups, which is also "generally preferred" over SSL_get_negotiated_group()
as documented in OpenSSL git commit 93d4f6133f.

This change makes external groups listing by name using SSL_group_to_name()
available since OpenSSL 3.0.  To preserve "prime256v1" naming for the group
0x0017, and to avoid breaking BoringSSL and older OpenSSL versions support,
it is used supplementary for a group that appears to be unknown.

See https://github.com/openssl/openssl/issues/27137 for related discussion.
2025-04-10 18:51:10 +04:00
Sergey Kandaurov 6c3a9d5612 Upstream: fixed passwords support for dynamic certificates.
Passwords were not preserved in optimized SSL contexts, the bug had
appeared in d791b4aab (1.23.1), as in the following configuration:

    server {
        proxy_ssl_password_file password;
        proxy_ssl_certificate $ssl_server_name.crt;
        proxy_ssl_certificate_key $ssl_server_name.key;

        location /original/ {
            proxy_pass https://u1/;
        }

        location /optimized/ {
            proxy_pass https://u2/;
        }
    }

The fix is to always preserve passwords, by copying to the configuration
pool, if dynamic certificates are used.  This is done as part of merging
"ssl_passwords" configuration.

To minimize the number of copies, a preserved version is then used for
inheritance.  A notable exception is inheritance of preserved empty
passwords to the context with statically configured certificates:

    server {
        proxy_ssl_certificate $ssl_server_name.crt;
        proxy_ssl_certificate_key $ssl_server_name.key;

        location / {
            proxy_pass ...;

            proxy_ssl_certificate example.com.crt;
            proxy_ssl_certificate_key example.com.key;
        }
    }

In this case, an unmodified version (NULL) of empty passwords is set,
to allow reading them from the password prompt on nginx startup.

As an additional optimization, a preserved instance of inherited
configured passwords is set to the previous level, to inherit it
to other contexts:

    server {
        proxy_ssl_password_file password;

        location /1/ {
            proxy_pass https://u1/;
            proxy_ssl_certificate $ssl_server_name.crt;
            proxy_ssl_certificate_key $ssl_server_name.key;
        }

        location /2/ {
            proxy_pass https://u2/;
            proxy_ssl_certificate $ssl_server_name.crt;
            proxy_ssl_certificate_key $ssl_server_name.key;
        }
    }
2025-04-10 17:27:45 +04:00
Sergey Kandaurov a813c63921 Charset filter: improved validation of charset_map with utf-8.
It was possible to write outside of the buffer used to keep UTF-8
decoded values when parsing conversion table configuration.

Since this happened before UTF-8 decoding, the fix is to check in
advance if character codes are of more than 3-byte sequence.  Note
that this is already enforced by a later check for ngx_utf8_decode()
decoded values for 0xffff, which corresponds to the maximum value
encoded as a valid 3-byte sequence, so the fix does not affect the
valid values.

Found with AddressSanitizer.
Fixes GitHub issue #529.
2025-04-09 19:37:51 +04:00
Sergey Kandaurov d313056537 Slice filter: improved memory allocation error handling.
As uncovered by recent addition in slice.t, a partially initialized
context, coupled with HTTP 206 response from stub backend, might be
accessed in the next slice subrequest.

Found by bad memory allocator simulation.
2025-03-10 19:32:07 +03:00
Sergey Kandaurov d16251969b SSL: removed stale comments.
It appears to be a relic from prototype locking removed in b0b7b5a35.
2025-02-26 17:40:03 +04:00
Sergey Kandaurov 311c390377 SSL: improved logging of saving sessions from upstream servers.
This makes it easier to understand why sessions may not be saved
in shared memory due to size.
2025-02-26 17:40:03 +04:00
Sergey Kandaurov 9124592202 SSL: raised limit for sessions stored in shared memory.
Upstream SSL sessions may be of a noticeably larger size with tickets
in TLSv1.2 and older versions, or with "stateless" tickets in TLSv1.3,
if a client certificate is saved into the session.  Further, certain
stateless session resumption implemetations may store additional data.

Such one is JDK, known to also include server certificates in session
ticket data, which roughly doubles a decoded session size to slightly
beyond the previous limit.  While it's believed to be an issue on the
JDK side, this change allows to save such sessions.

Another, innocent case is using RSA certificates with 8192 key size.
2025-02-26 17:40:03 +04:00
Sergey Kandaurov 3d7304b527 SSL: using static storage for NGX_SSL_MAX_SESSION_SIZE buffers.
All such transient buffers are converted to the single storage in BSS.

In preparation to raise the limit.
2025-02-26 17:40:03 +04:00
Sergey Kandaurov b11ae4cfc9 SSL: style. 2025-02-26 17:40:03 +04:00
Sergey Kandaurov d25139db01 Improved ngx_http_subrequest() error handling.
Previously, request might be left in inconsistent state in case of error,
which manifested in "http request count is zero" alerts when used by SSI
filter.

The fix is to reshuffle initialization order to postpone committing state
changes until after any potentially failing parts.

Found by bad memory allocator simulation.
2025-02-21 00:04:12 +04:00
Orgad Shaneh f51e2de6fe Add gitignore file. 2025-02-20 14:42:53 +03:00
Thierry Bastian 3327353ec0 Configure: MSVC compatibility with PCRE2 10.45. 2025-02-18 19:07:11 +04:00
Piotr Sikora 9a4090f02a Core: fix build without libcrypt.
libcrypt is no longer part of glibc, so it might not be available.

Signed-off-by: Piotr Sikora <piotr@aviatrix.com>
2025-02-18 16:18:10 +03:00
Sergey Kandaurov f274b3f72f Version bump. 2025-02-18 15:49:18 +04:00
Sergey Kandaurov ecb809305e nginx-1.27.4-RELEASE 2025-02-05 20:13:42 +04:00
Sergey Kandaurov 46b9f5d389 SNI: added restriction for TLSv1.3 cross-SNI session resumption.
In OpenSSL, session resumption always happens in the default SSL context,
prior to invoking the SNI callback.  Further, unlike in TLSv1.2 and older
protocols, SSL_get_servername() returns values received in the resumption
handshake, which may be different from the value in the initial handshake.
Notably, this makes the restriction added in b720f650b insufficient for
sessions resumed with different SNI server name.

Considering the example from b720f650b, previously, a client was able to
request example.org by presenting a certificate for example.org, then to
resume and request example.com.

The fix is to reject handshakes resumed with a different server name, if
verification of client certificates is enabled in a corresponding server
configuration.
2025-02-05 20:11:42 +04:00
Roman Arutyunyan 22a2a225ba Added "keepalive_min_timeout" directive.
The directive sets a timeout during which a keepalive connection will
not be closed by nginx for connection reuse or graceful shutdown.

The change allows clients that send multiple requests over the same
connection without delay or with a small delay between them, to avoid
receiving a TCP RST in response to one of them.  This excludes network
issues and non-graceful shutdown.  As a side-effect, it also addresses
the TCP reset problem described in RFC 9112, Section 9.6, when the last
sent HTTP response could be damaged by a followup TCP RST.  It is important
for non-idempotent requests, which cannot be retried by client.

It is not recommended to set keepalive_min_timeout to large values as
this can introduce an additional delay during graceful shutdown and may
restrict nginx from effective connection reuse.
2025-02-05 13:08:01 +03:00
Sergey Kandaurov 04914cfbcb Misc: moved documentation in generated ZIP archive.
The recently added GitHub files now reside in the docs directory.
2025-01-30 18:21:43 +04:00
Sergey Kandaurov e715202220 Configure: fixed --with-libatomic=DIR with recent libatomic_ops.
The build location of the resulting libatomic_ops.a was changed in v7.4.0
after converting libatomic_ops to use libtool.  The fix is to use library
from the install path, this allows building with both old and new versions.

Initially reported here:
https://mailman.nginx.org/pipermail/nginx/2018-April/056054.html
2025-01-30 17:16:10 +04:00
Aleksei Bavshin 64d0795ac4 QUIC: added missing casts in iov_base assignments.
This is consistent with the rest of the code and fixes build on systems
with non-standard definition of struct iovec (Solaris, Illumos).
2025-01-28 08:00:42 -08:00
Pavel Pautov 5ab4f32e9d Upstream: fixed --with-compat build without SSL, broken by 454ad0e. 2025-01-23 10:50:13 -08:00
Sergey Kandaurov 5d5d9adccf SSL: avoid using mismatched certificate/key cached pairs.
This can happen with certificates and certificate keys specified
with variables due to partial cache update in various scenarios:
- cache expiration with only one element of pair evicted
- on-disk update with non-cacheable encrypted keys
- non-atomic on-disk update

The fix is to retry with fresh data on X509_R_KEY_VALUES_MISMATCH.
2025-01-17 04:37:46 +04:00
Sergey Kandaurov 454ad0ef33 Upstream: caching certificates and certificate keys with variables.
Caching is enabled with proxy_ssl_certificate_cache and friends.

Co-authored-by: Aleksei Bavshin <a.bavshin@nginx.com>
2025-01-17 04:37:46 +04:00
Sergey Kandaurov 4b96ad14f3 SSL: cache revalidation of file based dynamic certificates.
Revalidation is based on file modification time and uniq file index,
and happens after the cache object validity time is expired.
2025-01-17 04:37:46 +04:00
Sergey Kandaurov 0e756d67aa SSL: caching certificates and certificate keys with variables.
A new directive "ssl_certificate_cache max=N [valid=time] [inactive=time]"
enables caching of SSL certificate chain and secret key objects specified
by "ssl_certificate" and "ssl_certificate_key" directives with variables.

Co-authored-by: Aleksei Bavshin <a.bavshin@nginx.com>
2025-01-17 04:37:46 +04:00
Sergey Kandaurov 7677d5646a SSL: encrypted certificate keys are exempt from object cache.
SSL object cache, as previously introduced in 1.27.2, did not take
into account encrypted certificate keys that might be unexpectedly
fetched from the cache regardless of the matching passphrase.  To
avoid this, caching of encrypted certificate keys is now disabled
based on the passphrase callback invocation.

A notable exception is encrypted certificate keys configured without
ssl_password_file.  They are loaded once resulting in the passphrase
prompt on startup and reused in other contexts as applicable.
2025-01-17 04:37:46 +04:00
Sergey Kandaurov 8311e14ae6 SSL: object cache inheritance from the old configuration cycle.
Memory based objects are always inherited, engine based objects are
never inherited to adhere the volatile nature of engines, file based
objects are inherited subject to modification time and file index.

The previous behaviour to bypass cache from the old configuration cycle
is preserved with a new directive "ssl_object_cache_inheritable off;".
2025-01-17 04:37:46 +04:00
Daniel Vasquez Lopez 47f862ffad Slice filter: log the expected range in case of range error. 2025-01-16 21:09:59 +04:00
Sergey Kandaurov 57d54fd922 Gzip: compatibility with recent zlib-ng 2.2.x versions.
It now uses 5/4 times more memory for the pending buffer.

Further, a single allocation is now used, which takes additional 56 bytes
for deflate_allocs in 64-bit mode aligned to 16, to store sub-allocation
pointers, and the total allocation size now padded up to 128 bytes, which
takes theoretically 200 additional bytes in total.  This fits though into
"4 * (64 + sizeof(void*))" additional space for ZALLOC used in zlib-ng
2.1.x versions.  The comment was updated to reflect this.
2025-01-09 17:19:24 +04:00
Roman Arutyunyan febe6e728f Year 2025. 2025-01-09 17:08:02 +04:00
Roman Arutyunyan e3a9b6ad08 QUIC: fixed accessing a released stream.
While trying to close a stream in ngx_quic_close_streams() by calling its
read event handler, the next stream saved prior to that could be destroyed
recursively.  This caused a segfault while trying to access the next stream.

The way the next stream could be destroyed in HTTP/3 is the following.
A request stream read event handler ngx_http_request_handler() could
end up calling ngx_http_v3_send_cancel_stream() to report a cancelled
request stream in the decoder stream.  If sending stream cancellation
decoder instruction fails for any reason, and the decoder stream is the
next in order after the request stream, the issue is triggered.

The fix is to postpone calling read event handlers for all streams being
closed to avoid closing a released stream.
2024-12-27 16:14:14 +04:00
Roman Arutyunyan a52ba8ba0e QUIC: ignore version negotiation packets.
Previously, such packets were treated as long header packets with unknown
version 0, and a version negotiation packet was sent in response.  This
could be used to set up an infinite traffic reflect loop with another nginx
instance.

Now version negotiation packets are ignored.  As per RFC 9000, Section 6.1:

  An endpoint MUST NOT send a Version Negotiation packet in response to
  receiving a Version Negotiation packet.
2024-12-26 18:58:05 +04:00
Jordan Zebor c73fb273ac Updated security policy to clarify experimental features.
The original security policy language did not capture the scope
as intended for experimental features and availability.
2024-12-23 20:36:15 +04:00
nandsky 930caed3bf QUIC: fixed client request timeout in 0-RTT scenarios.
Since 0-RTT and 1-RTT data exist in the same packet number space,
ngx_quic_discard_ctx incorrectly discards 1-RTT packets when
0-RTT keys are discarded.

The issue was introduced by 58b92177e7.
2024-12-10 17:17:20 +04:00
Roman Arutyunyan e28ef42b97 Version bump. 2024-11-27 20:07:29 +04:00
93 changed files with 4837 additions and 816 deletions

41
.github/workflows/f5_cla.yml vendored Normal file
View File

@ -0,0 +1,41 @@
---
name: F5 CLA
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened, closed, synchronize]
permissions: read-all
jobs:
f5-cla:
name: F5 CLA
runs-on: ubuntu-24.04
permissions:
actions: write
pull-requests: write
statuses: write
steps:
- name: Run F5 Contributor License Agreement (CLA) assistant
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have hereby read the F5 CLA and agree to its terms') || github.event_name == 'pull_request_target'
uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1
with:
# Path to the CLA document.
path-to-document: https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md
# Custom CLA messages.
custom-notsigned-prcomment: '🎉 Thank you for your contribution! It appears you have not yet signed the [F5 Contributor License Agreement (CLA)](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md), which is required for your changes to be incorporated into an F5 Open Source Software (OSS) project. Please kindly read the [F5 CLA](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md) and reply on a new comment with the following text to agree:'
custom-pr-sign-comment: 'I have hereby read the F5 CLA and agree to its terms'
custom-allsigned-prcomment: '✅ All required contributors have signed the F5 CLA for this PR. Thank you!'
# Remote repository storing CLA signatures.
remote-organization-name: f5
remote-repository-name: f5-cla-data
# Branch where CLA signatures are stored.
branch: main
path-to-signatures: signatures/signatures.json
# Comma separated list of usernames for maintainers or any other individuals who should not be prompted for a CLA.
# NOTE: You will want to edit the usernames to suit your project needs.
allowlist: bot*
# Do not lock PRs after a merge.
lock-pullrequest-aftermerge: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PERSONAL_ACCESS_TOKEN: ${{ secrets.F5_CLA_TOKEN }}

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/Makefile
/objs/
/tmp/

View File

@ -1,5 +1,5 @@
Copyright (C) 2002-2021 Igor Sysoev
Copyright (C) 2011-2024 Nginx, Inc.
Copyright (C) 2011-2025 Nginx, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -69,8 +69,7 @@ limiting, or buffer size configurations, or applying changes is impractical.
Availability issues excluded from the security release process:
- Local file content or upstream response content resulting only in worker
process termination.
- Issues with experimental features which result only in worker process
termination.
- Issues with experimental features which result only in availability impact.
## Trusted Configurations and Misconfigurations

View File

@ -26,6 +26,10 @@ ngx_msvc_ver=`echo $NGX_MSVC_VER | sed -e 's/^\([0-9]*\).*/\1/'`
case "$NGX_MSVC_VER" in
*ARM64)
NGX_MACHINE=arm64
;;
*x64)
NGX_MACHINE=amd64
;;

View File

@ -7,8 +7,8 @@ if [ $NGX_LIBATOMIC != YES ]; then
have=NGX_HAVE_LIBATOMIC . auto/have
CORE_INCS="$CORE_INCS $NGX_LIBATOMIC/src"
LINK_DEPS="$LINK_DEPS $NGX_LIBATOMIC/src/libatomic_ops.a"
CORE_LIBS="$CORE_LIBS $NGX_LIBATOMIC/src/libatomic_ops.a"
LINK_DEPS="$LINK_DEPS $NGX_LIBATOMIC/build/lib/libatomic_ops.a"
CORE_LIBS="$CORE_LIBS $NGX_LIBATOMIC/build/lib/libatomic_ops.a"
else

View File

@ -3,14 +3,19 @@
# Copyright (C) Nginx, Inc.
case $NGX_LIBATOMIC in
/*) ngx_prefix="$NGX_LIBATOMIC/build" ;;
*) ngx_prefix="$PWD/$NGX_LIBATOMIC/build" ;;
esac
cat << END >> $NGX_MAKEFILE
$NGX_LIBATOMIC/src/libatomic_ops.a: $NGX_LIBATOMIC/Makefile
cd $NGX_LIBATOMIC && \$(MAKE)
$NGX_LIBATOMIC/build/lib/libatomic_ops.a: $NGX_LIBATOMIC/Makefile
cd $NGX_LIBATOMIC && \$(MAKE) && \$(MAKE) install
$NGX_LIBATOMIC/Makefile: $NGX_MAKEFILE
cd $NGX_LIBATOMIC \\
&& if [ -f Makefile ]; then \$(MAKE) distclean; fi \\
&& ./configure
&& ./configure --prefix=$ngx_prefix
END

View File

@ -12,7 +12,6 @@ if [ $OPENSSL != NONE ]; then
if [ $USE_OPENSSL_QUIC = YES ]; then
have=NGX_QUIC . auto/have
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
fi
case "$CC" in
@ -148,14 +147,18 @@ else
if [ $USE_OPENSSL_QUIC = YES ]; then
ngx_feature="OpenSSL QUIC support"
ngx_feature="OpenSSL QUIC API"
ngx_feature_name="NGX_QUIC"
ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
ngx_feature_test="SSL_set_quic_tls_cbs(NULL, NULL, NULL)"
. auto/feature
if [ $ngx_found = no ]; then
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
ngx_feature="BoringSSL-like QUIC API"
ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
. auto/feature
fi
if [ $ngx_found = no ]; then
ngx_feature="OpenSSL QUIC compatibility"
ngx_feature_test="SSL_CTX_add_custom_ext(NULL, 0, 0,
NULL, NULL, NULL, NULL, NULL)"

View File

@ -13,6 +13,10 @@ case "$CC" in
OPENSSL_TARGET=VC-WIN64A
;;
arm64)
OPENSSL_TARGET=VC-WIN64-ARM
;;
*)
OPENSSL_TARGET=VC-WIN32
;;

View File

@ -37,6 +37,7 @@ if [ $PCRE_LIBRARY = PCRE2 ]; then
pcre2_xclass.c"
ngx_pcre_test="pcre2_chkdint.c \
pcre2_compile_class.c \
pcre2_convert.c \
pcre2_extuni.c \
pcre2_find_bracket.c \

View File

@ -118,3 +118,19 @@ ngx_feature_libs=
ngx_feature_test="int32_t lock = 0;
if (!OSAtomicCompareAndSwap32Barrier(0, 1, &lock)) return 1"
. auto/feature
ngx_feature="TCP_KEEPALIVE"
ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
ngx_feature_run=no
ngx_feature_incs="#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPALIVE, NULL, 0);
setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
. auto/feature
NGX_KEEPALIVE_CHECKED=YES

View File

@ -129,28 +129,8 @@ if test -z "$NGX_KQUEUE_CHECKED"; then
fi
if [ "$NGX_SYSTEM" = "NetBSD" ]; then
# NetBSD 2.0 incompatibly defines kevent.udata as "intptr_t"
cat << END >> $NGX_AUTO_CONFIG_H
#define NGX_KQUEUE_UDATA_T
END
else
cat << END >> $NGX_AUTO_CONFIG_H
#define NGX_KQUEUE_UDATA_T (void *)
END
fi
ngx_feature="crypt()"
ngx_feature_name=
ngx_feature_name="NGX_HAVE_CRYPT"
ngx_feature_run=no
ngx_feature_incs=
ngx_feature_path=
@ -162,7 +142,7 @@ ngx_feature_test="crypt(\"test\", \"salt\");"
if [ $ngx_found = no ]; then
ngx_feature="crypt() in libcrypt"
ngx_feature_name=
ngx_feature_name="NGX_HAVE_CRYPT"
ngx_feature_run=no
ngx_feature_incs=
ngx_feature_path=
@ -508,18 +488,20 @@ ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_DEFER_ACCEPT, NULL, 0)"
. auto/feature
ngx_feature="TCP_KEEPIDLE"
ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
ngx_feature_run=no
ngx_feature_incs="#include <sys/socket.h>
if test -z "$NGX_KEEPALIVE_CHECKED"; then
ngx_feature="TCP_KEEPIDLE"
ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
ngx_feature_run=no
ngx_feature_incs="#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0);
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0);
setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
. auto/feature
. auto/feature
fi
ngx_feature="TCP_FASTOPEN"

View File

@ -36,8 +36,6 @@ http {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {

View File

@ -5,6 +5,466 @@
<change_log title="nginx">
<changes ver="1.29.2" date="2025-10-07">
<change type="feature">
<para lang="ru">
теперь nginx можно собрать с AWS-LC.<br/>
Спасибо Samuel Chiang.
</para>
<para lang="en">
now nginx can be built with AWS-LC.<br/>
Thanks Samuel Chiang.
</para>
</change>
<change type="bugfix">
<para lang="ru">
теперь директива ssl_protocols работает
в виртуальном сервере, отличном от сервера по умолчанию,
при использовании OpenSSL 1.1.1 и новее.
</para>
<para lang="en">
now the "ssl_protocols" directive works
in a virtual server different from the default server
when using OpenSSL 1.1.1 or newer.
</para>
</change>
<change type="bugfix">
<para lang="ru">
при использовании TLSv1.3 с OpenSSL и клиентских сертификатов
SSL handshake всегда завершался ошибкой
при восстановлении сессии с другим значением SNI;
ошибка появилась в 1.27.4.
</para>
<para lang="en">
SSL handshake always failed
when using TLSv1.3 with OpenSSL and client certificates
and resuming a session with a different SNI value;
the bug had appeared in 1.27.4.
</para>
</change>
<change type="bugfix">
<para lang="ru">
при использовании QUIC и директивы ssl_reject_handshake
в логах могли появляться сообщения
"ignoring stale global SSL error";
ошибка появилась в 1.29.0.<br/>
Спасибо Владимиру Хомутову.
</para>
<para lang="en">
the "ignoring stale global SSL error"
alerts might appear in logs
when using QUIC and the "ssl_reject_handshake" directive;
the bug had appeared in 1.29.0.<br/>
Thanks to Vladimir Homutov.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в обработке delta-seconds
в строке "Cache-Control" в заголовке ответа бэкенда.
</para>
<para lang="en">
in delta-seconds processing
in the "Cache-Control" backend response header line.
</para>
</change>
<change type="bugfix">
<para lang="ru">
команда XCLIENT не использовала кодировку xtext.<br/>
Спасибо Igor Morgenstern из Aisle Research.
</para>
<para lang="en">
an XCLIENT command didn't use the xtext encoding.<br/>
Thanks to Igor Morgenstern of Aisle Research.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в кешировании SSL-сертификатов во время переконфигурации.
</para>
<para lang="en">
in SSL certificate caching during reconfiguration.
</para>
</change>
</changes>
<changes ver="1.29.1" date="2025-08-13">
<change type="security">
<para lang="ru">
обработка специально созданного логина/пароля при использовании
метода аутентификации "none" в модуле ngx_mail_smtp_module
могла приводить к отправке серверу аутентификации
части содержимого памяти рабочего процесса (CVE-2025-53859).
</para>
<para lang="en">
processing of a specially crafted login/password when using
the "none" authentication method in the ngx_mail_smtp_module
might cause worker process memory disclosure
to the authentication server (CVE-2025-53859).
</para>
</change>
<change type="change">
<para lang="ru">
теперь сжатие сертификатов в протоколе TLSv1.3 по умолчанию запрещено.
</para>
<para lang="en">
now TLSv1.3 certificate compression is disabled by default.
</para>
</change>
<change type="feature">
<para lang="ru">
директива ssl_certificate_compression.
</para>
<para lang="en">
the "ssl_certificate_compression" directive.
</para>
</change>
<change type="feature">
<para lang="ru">
поддержка 0-RTT в QUIC при использовании OpenSSL 3.5.1 и новее.
</para>
<para lang="en">
support for 0-RTT in QUIC when using OpenSSL 3.5.1 or newer.
</para>
</change>
<change type="bugfix">
<para lang="ru">
при использовании HTTP/2 и директивы early_hints
ответ 103 мог буферизироваться.
</para>
<para lang="en">
the 103 response might be buffered
when using HTTP/2 and the "early_hints" directive.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в обработке заголовков запроса "Host" и ":authority"
с одинаковыми значениями при использовании HTTP/2;
ошибка появилась в 1.17.9.
</para>
<para lang="en">
in handling "Host" and ":authority" header lines
with equal values when using HTTP/2;
the bug had appeared in 1.17.9.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в обработке заголовка запроса "Host" с портом
при использовании HTTP/3.
</para>
<para lang="en">
in handling "Host" header lines with a port
when using HTTP/3.
</para>
</change>
<change type="bugfix">
<para lang="ru">
nginx не собирался под NetBSD 10.0.
</para>
<para lang="en">
nginx could not be built on NetBSD 10.0.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в работе параметра none директивы smtp_auth.
</para>
<para lang="en">
in the "none" parameter of the "smtp_auth" directive.
</para>
</change>
</changes>
<changes ver="1.29.0" date="2025-06-24">
<change type="feature">
<para lang="ru">
поддержка ответа с кодом 103 от proxy- и gRPC-бэкендов;
директива early_hints.
</para>
<para lang="en">
support for response code 103 from proxy and gRPC backends;
the "early_hints" directive.
</para>
</change>
<change type="feature">
<para lang="ru">
возможность загрузки секретных ключей с аппаратных устройств
с помощью OpenSSL provider.
</para>
<para lang="en">
loading of secret keys from hardware tokens
with OpenSSL provider.
</para>
</change>
<change type="feature">
<para lang="ru">
поддержка параметра so_keepalive директивы listen на macOS.
</para>
<para lang="en">
support for the "so_keepalive" parameter of the "listen" directive on macOS.
</para>
</change>
<change type="change">
<para lang="ru">
уровень логгирования ошибок SSL в QUIC handshake
изменён с уровня error на crit для критических ошибок
и на info для всех остальных;
уровень логгирования неподдерживаемых транспортных параметров QUIC
понижен с уровня info до debug.
</para>
<para lang="en">
the logging level of SSL errors in a QUIC handshake
has been changed from "error" to "crit" for critical errors,
and to "info" for the rest;
the logging level of unsupported QUIC transport parameters
has been lowered from "info" to "debug".
</para>
</change>
<change type="change">
<para lang="ru">
бинарная версия nginx/Windows теперь использует для сборки Windows SDK 10.
</para>
<para lang="en">
the native nginx/Windows binary release is now built using Windows SDK 10.
</para>
</change>
<change type="bugfix">
<para lang="ru">
nginx не собирался gcc 15,
если использовались модули ngx_http_v2_module и ngx_http_v3_module.
</para>
<para lang="en">
nginx could not be built by gcc 15
if ngx_http_v2_module or ngx_http_v3_module modules were used.
</para>
</change>
<change type="bugfix">
<para lang="ru">
nginx мог не собираться gcc 14 и новее с оптимизацией -O3 -flto,
если использовался модуль ngx_http_v3_module.
</para>
<para lang="en">
nginx might not be built by gcc 14 or newer with -O3 -flto optimization
if ngx_http_v3_module was used.
</para>
</change>
<change>
<para lang="ru">
Исправления и улучшения в HTTP/3.
</para>
<para lang="en">
Bugfixes and improvements in HTTP/3.
</para>
</change>
</changes>
<changes ver="1.27.5" date="2025-04-16">
<change type="feature">
<para lang="ru">
контроль перегрузки CUBIC в соединениях QUIC.
</para>
<para lang="en">
CUBIC congestion control in QUIC connections.
</para>
</change>
<change type="change">
<para lang="ru">
ограничение на максимальный размер кешируемых в разделяемой памяти
SSL-сессий поднято до 8192.
</para>
<para lang="en">
the maximum size limit for SSL sessions cached in shared memory
has been raised to 8192.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в директивах grpc_ssl_password_file, proxy_ssl_password_file и
uwsgi_ssl_password_file
при загрузке SSL-сертификатов и зашифрованных ключей из переменных;
ошибка появилась в 1.23.1.
</para>
<para lang="en">
in the "grpc_ssl_password_file", "proxy_ssl_password_file", and
"uwsgi_ssl_password_file" directives
when loading SSL certificates and encrypted keys from variables;
the bug had appeared in 1.23.1.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в переменных $ssl_curve и $ssl_curves
при использовании подключаемых кривых в OpenSSL.
</para>
<para lang="en">
in the $ssl_curve and $ssl_curves variables
when using pluggable curves in OpenSSL.
</para>
</change>
<change type="bugfix">
<para lang="ru">
nginx не собирался с musl libc.<br/>
Спасибо Piotr Sikora.
</para>
<para lang="en">
nginx could not be built with musl libc.<br/>
Thanks to Piotr Sikora.
</para>
</change>
<change>
<para lang="ru">
Улучшения производительности и исправления в HTTP/3.
</para>
<para lang="en">
Performance improvements and bugfixes in HTTP/3.
</para>
</change>
</changes>
<changes ver="1.27.4" date="2025-02-05">
<change type="security">
<para lang="ru">
недостаточная проверка в обработке виртуальных серверов
при использовании SNI в TLSv1.3 позволяла повторно использовать
SSL-сессию в контексте другого виртуального сервера,
чтобы обойти проверку клиентских SSL-сертификатов (CVE-2025-23419).
</para>
<para lang="en">
insufficient check in virtual servers handling with TLSv1.3 SNI
allowed to reuse SSL sessions in a different virtual server,
to bypass client SSL certificates verification (CVE-2025-23419).
</para>
</change>
<change type="feature">
<para lang="ru">
директивы ssl_object_cache_inheritable, ssl_certificate_cache,
proxy_ssl_certificate_cache, grpc_ssl_certificate_cache
и uwsgi_ssl_certificate_cache.
</para>
<para lang="en">
the "ssl_object_cache_inheritable", "ssl_certificate_cache",
"proxy_ssl_certificate_cache", "grpc_ssl_certificate_cache",
and "uwsgi_ssl_certificate_cache" directives.
</para>
</change>
<change type="feature">
<para lang="ru">
директива keepalive_min_timeout.
</para>
<para lang="en">
the "keepalive_min_timeout" directive.
</para>
</change>
<change type="workaround">
<para lang="ru">
при использовании zlib-ng
в логах появлялись сообщения "gzip filter failed to use preallocated memory".
</para>
<para lang="en">
"gzip filter failed to use preallocated memory" alerts appeared in logs
when using zlib-ng.
</para>
</change>
<change type="bugfix">
<para lang="ru">
nginx не мог собрать библиотеку libatomic из исходных текстов,
если использовался параметр --with-libatomic=DIR.
</para>
<para lang="en">
nginx could not build libatomic library using the library sources
if the --with-libatomic=DIR option was used.
</para>
</change>
<change type="bugfix">
<para lang="ru">
могла происходить ошибка установления соединения
при использовании 0-RTT в QUIC;
ошибка появилась в 1.27.1.
</para>
<para lang="en">
QUIC connection might not be established when using 0-RTT;
the bug had appeared in 1.27.1.
</para>
</change>
<change type="bugfix">
<para lang="ru">
теперь nginx игнорирует пакеты согласования версий QUIC от клиентов.
</para>
<para lang="en">
nginx now ignores QUIC version negotiation packets from clients.
</para>
</change>
<change type="bugfix">
<para lang="ru">
nginx не собирался на Solaris 10 и более ранних
с модулем ngx_http_v3_module.
</para>
<para lang="en">
nginx could not be built on Solaris 10 and earlier
with the ngx_http_v3_module.
</para>
</change>
<change>
<para lang="ru">
Исправления в HTTP/3.
</para>
<para lang="en">
Bugfixes in HTTP/3.
</para>
</change>
</changes>
<changes ver="1.27.3" date="2024-11-26">
<change type="feature">
@ -8368,7 +8828,7 @@ Thanks to Piotr Sikora.
Спасибо Piotr Sikora.
</para>
<para lang="en">
now nginx can be build with BoringSSL and LibreSSL.<br/>
now nginx can be built with BoringSSL and LibreSSL.<br/>
Thanks to Piotr Sikora.
</para>
</change>
@ -25651,7 +26111,7 @@ the ECONNABORTED error log level was changed to "error" from "crit".
модуль ngx_http_perl_module не собирался без модуля ngx_http_ssi_filter_module.
</para>
<para lang="en">
the ngx_http_perl_module could not be build without
the ngx_http_perl_module could not be built without
the ngx_http_ssi_filter_module.
</para>
</change>

View File

@ -6,9 +6,9 @@ TEMP = tmp
CC = cl
OBJS = objs.msvc8
OPENSSL = openssl-3.0.15
OPENSSL = openssl-3.5.4
ZLIB = zlib-1.3.1
PCRE = pcre2-10.39
PCRE = pcre2-10.46
release: export
@ -76,7 +76,8 @@ win32:
--with-stream_realip_module \
--with-stream_ssl_preread_module \
--with-openssl=$(OBJS)/lib/$(OPENSSL) \
--with-openssl-opt="no-asm no-tests -D_WIN32_WINNT=0x0501" \
--with-openssl-opt="no-asm no-tests no-makedepend \
-D_WIN32_WINNT=0x0501" \
--with-http_ssl_module \
--with-mail_ssl_module \
--with-stream_ssl_module
@ -93,6 +94,9 @@ zip: export
mv $(TEMP)/$(NGINX)/LICENSE $(TEMP)/$(NGINX)/docs.new
mv $(TEMP)/$(NGINX)/README.md $(TEMP)/$(NGINX)/docs.new
mv $(TEMP)/$(NGINX)/CODE_OF_CONDUCT.md $(TEMP)/$(NGINX)/docs.new
mv $(TEMP)/$(NGINX)/CONTRIBUTING.md $(TEMP)/$(NGINX)/docs.new
mv $(TEMP)/$(NGINX)/SECURITY.md $(TEMP)/$(NGINX)/docs.new
mv $(TEMP)/$(NGINX)/docs/html $(TEMP)/$(NGINX)
rm -r $(TEMP)/$(NGINX)/docs
@ -106,7 +110,7 @@ zip: export
cp -p $(OBJS)/lib/$(OPENSSL)/LICENSE.txt \
$(TEMP)/$(NGINX)/docs/OpenSSL.LICENSE
cp -p $(OBJS)/lib/$(PCRE)/LICENCE \
cp -p $(OBJS)/lib/$(PCRE)/LICENCE.md \
$(TEMP)/$(NGINX)/docs/PCRE.LICENCE
sed -ne '/^ (C) 1995-20/,/^ jloup@gzip\.org/p' \

View File

@ -3,7 +3,7 @@ make -f misc/GNUmakefile release
the required tools:
*) xsltproc to build CHANGES,
*) xslscript.pl ( http://hg.nginx.org/xslscript ) to build XSLTs
*) xslscript.pl ( https://github.com/nginx/xslscript ) to build XSLTs
from XSLScript sources.

View File

@ -9,8 +9,8 @@
#define _NGINX_H_INCLUDED_
#define nginx_version 1027003
#define NGINX_VERSION "1.27.3"
#define nginx_version 1029003
#define NGINX_VERSION "1.29.3"
#define NGINX_VER "nginx/" NGINX_VERSION
#ifdef NGX_BUILD

View File

@ -94,7 +94,7 @@ typedef intptr_t ngx_flag_t;
#ifndef NGX_ALIGNMENT
#define NGX_ALIGNMENT sizeof(unsigned long) /* platform word */
#define NGX_ALIGNMENT sizeof(uintptr_t) /* platform word */
#endif
#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))

View File

@ -765,6 +765,8 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
#if !(NGX_DARWIN)
if (ls[i].keepidle) {
value = ls[i].keepidle;
@ -782,6 +784,8 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
}
}
#endif
if (ls[i].keepintvl) {
value = ls[i].keepintvl;

View File

@ -26,6 +26,7 @@ typedef struct ngx_event_aio_s ngx_event_aio_t;
typedef struct ngx_connection_s ngx_connection_t;
typedef struct ngx_thread_task_s ngx_thread_task_t;
typedef struct ngx_ssl_s ngx_ssl_t;
typedef struct ngx_ssl_cache_s ngx_ssl_cache_t;
typedef struct ngx_proxy_protocol_s ngx_proxy_protocol_t;
typedef struct ngx_quic_stream_s ngx_quic_stream_t;
typedef struct ngx_ssl_connection_s ngx_ssl_connection_t;

View File

@ -1494,8 +1494,9 @@ ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)
uintptr_t
ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
{
ngx_uint_t n;
u_char prefix;
uint32_t *escape;
ngx_uint_t n;
static u_char hex[] = "0123456789ABCDEF";
/*
@ -1633,11 +1634,36 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
/* mail_auth is the same as memcached */
/* " ", "+", "=", not allowed */
static uint32_t mail_xtext[] = {
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
0x20000801, /* 0010 0000 0000 0000 0000 1000 0000 0001 */
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
};
static uint32_t *map[] =
{ uri, args, uri_component, html, refresh, memcached, memcached };
{ uri, args, uri_component, html, refresh, memcached, memcached,
mail_xtext };
static u_char map_char[] =
{ '%', '%', '%', '%', '%', '%', '%', '+' };
escape = map[type];
prefix = map_char[type];
if (dst == NULL) {
@ -1658,7 +1684,7 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
while (size) {
if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
*dst++ = '%';
*dst++ = prefix;
*dst++ = hex[*src >> 4];
*dst++ = hex[*src & 0xf];
src++;

View File

@ -203,6 +203,7 @@ u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);
#define NGX_ESCAPE_REFRESH 4
#define NGX_ESCAPE_MEMCACHED 5
#define NGX_ESCAPE_MAIL_AUTH 6
#define NGX_ESCAPE_MAIL_XTEXT 7
#define NGX_UNESCAPE_URI 1
#define NGX_UNESCAPE_REDIRECT 2

View File

@ -207,7 +207,7 @@ ngx_thread_pool_exit_handler(void *data, ngx_log_t *log)
*lock = 0;
pthread_exit(0);
pthread_exit(NULL);
}

View File

@ -10,6 +10,15 @@
#include <ngx_event.h>
/* NetBSD up to 10.0 incompatibly defines kevent.udata as "intptr_t" */
#if (__NetBSD__ && __NetBSD_Version__ < 1000000000)
#define NGX_KQUEUE_UDATA_T
#else
#define NGX_KQUEUE_UDATA_T (void *)
#endif
typedef struct {
ngx_uint_t changes;
ngx_uint_t events;
@ -191,7 +200,7 @@ ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer)
kev.flags = EV_ADD|EV_ENABLE;
kev.fflags = 0;
kev.data = timer;
kev.udata = 0;
kev.udata = NGX_KQUEUE_UDATA_T (uintptr_t) 0;
ts.tv_sec = 0;
ts.tv_nsec = 0;
@ -237,7 +246,7 @@ ngx_kqueue_notify_init(ngx_log_t *log)
notify_kev.data = 0;
notify_kev.flags = EV_ADD|EV_CLEAR;
notify_kev.fflags = 0;
notify_kev.udata = 0;
notify_kev.udata = NGX_KQUEUE_UDATA_T (uintptr_t) 0;
if (kevent(ngx_kqueue, &notify_kev, 1, NULL, 0, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

View File

@ -203,6 +203,23 @@ ngx_event_accept(ngx_event_t *ev)
}
}
#if (NGX_HAVE_KEEPALIVE_TUNABLE && NGX_DARWIN)
/* Darwin doesn't inherit TCP_KEEPALIVE from a listening socket */
if (ls->keepidle) {
if (setsockopt(s, IPPROTO_TCP, TCP_KEEPALIVE,
(const void *) &ls->keepidle, sizeof(int))
== -1)
{
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
"setsockopt(TCP_KEEPALIVE, %d) failed, ignored",
ls->keepidle);
}
}
#endif
*log = ls->log;
c->recv = ngx_recv;

View File

@ -9,6 +9,10 @@
#include <ngx_core.h>
#include <ngx_event.h>
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
#include <zlib.h>
#endif
#define NGX_SSL_PASSWORD_BUFFER_SIZE 4096
@ -19,6 +23,13 @@ typedef struct {
static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void);
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
static int ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn,
CBB *out, const uint8_t *in, size_t in_len);
static void *ngx_ssl_cert_compression_alloc(void *opaque, u_int items,
u_int size);
static void ngx_ssl_cert_compression_free(void *opaque, void *address);
#endif
static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
int ret);
@ -45,8 +56,6 @@ static ssize_t ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file,
size_t size);
static void ngx_ssl_read_handler(ngx_event_t *rev);
static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
ngx_err_t err, char *text);
static void ngx_ssl_clear_error(ngx_log_t *log);
static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,
@ -130,6 +139,11 @@ int ngx_ssl_ticket_keys_index;
int ngx_ssl_ocsp_index;
int ngx_ssl_index;
int ngx_ssl_certificate_name_index;
int ngx_ssl_certificate_comp_index;
int ngx_ssl_client_hello_arg_index;
u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE];
ngx_int_t
@ -269,6 +283,21 @@ ngx_ssl_init(ngx_log_t *log)
return NGX_ERROR;
}
ngx_ssl_certificate_comp_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_certificate_comp_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_client_hello_arg_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,
NULL, NULL);
if (ngx_ssl_client_hello_arg_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
return NGX_OK;
}
@ -386,6 +415,11 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION);
#endif
#ifdef SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION);
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_RX_CERTIFICATE_COMPRESSION);
#endif
#ifdef SSL_OP_NO_ANTI_REPLAY
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_ANTI_REPLAY);
#endif
@ -443,10 +477,18 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
{
char *err;
X509 *x509, **elm;
u_long n;
EVP_PKEY *pkey;
ngx_uint_t mask;
STACK_OF(X509) *chain;
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT, &err, cert, NULL);
mask = 0;
elm = NULL;
retry:
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT | mask,
&err, cert, NULL);
if (chain == NULL) {
if (err != NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
@ -486,6 +528,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
}
}
if (elm == NULL) {
elm = ngx_array_push(&ssl->certs);
if (elm == NULL) {
X509_free(x509);
@ -493,6 +536,10 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
return NGX_ERROR;
}
} else {
X509_free(*elm);
}
*elm = x509;
/*
@ -513,11 +560,21 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
}
#else
{
int n;
/* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */
#ifdef SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS
/* OpenSSL 1.0.1+ */
SSL_CTX_clear_extra_chain_certs(ssl->ctx);
#else
if (ssl->ctx->extra_certs) {
sk_X509_pop_free(ssl->ctx->extra_certs, X509_free);
ssl->ctx->extra_certs = NULL;
}
#endif
n = sk_X509_num(chain);
while (n--) {
@ -533,10 +590,11 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
}
sk_X509_free(chain);
}
#endif
pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY, &err, key, passwords);
pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY | mask,
&err, key, passwords);
if (pkey == NULL) {
if (err != NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
@ -548,9 +606,23 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
}
if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
EVP_PKEY_free(pkey);
/* there can be mismatched pairs on uneven cache update */
n = ERR_peek_last_error();
if (ERR_GET_LIB(n) == ERR_LIB_X509
&& ERR_GET_REASON(n) == X509_R_KEY_VALUES_MISMATCH
&& mask == 0)
{
ERR_clear_error();
mask = NGX_SSL_CACHE_INVALIDATE;
goto retry;
}
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_PrivateKey(\"%s\") failed", key->data);
EVP_PKEY_free(pkey);
return NGX_ERROR;
}
@ -562,15 +634,23 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_int_t
ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords)
ngx_str_t *cert, ngx_str_t *key, ngx_ssl_cache_t *cache,
ngx_array_t *passwords)
{
char *err;
X509 *x509;
u_long n;
EVP_PKEY *pkey;
ngx_uint_t mask;
STACK_OF(X509) *chain;
chain = ngx_ssl_cache_connection_fetch(pool, NGX_SSL_CACHE_CERT, &err,
cert, NULL);
mask = 0;
retry:
chain = ngx_ssl_cache_connection_fetch(cache, pool,
NGX_SSL_CACHE_CERT | mask,
&err, cert, NULL);
if (chain == NULL) {
if (err != NULL) {
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
@ -610,8 +690,9 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
#endif
pkey = ngx_ssl_cache_connection_fetch(pool, NGX_SSL_CACHE_PKEY, &err,
key, passwords);
pkey = ngx_ssl_cache_connection_fetch(cache, pool,
NGX_SSL_CACHE_PKEY | mask,
&err, key, passwords);
if (pkey == NULL) {
if (err != NULL) {
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
@ -623,9 +704,23 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
}
if (SSL_use_PrivateKey(c->ssl->connection, pkey) == 0) {
EVP_PKEY_free(pkey);
/* there can be mismatched pairs on uneven cache update */
n = ERR_peek_last_error();
if (ERR_GET_LIB(n) == ERR_LIB_X509
&& ERR_GET_REASON(n) == X509_R_KEY_VALUES_MISMATCH
&& mask == 0)
{
ERR_clear_error();
mask = NGX_SSL_CACHE_INVALIDATE;
goto retry;
}
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
"SSL_use_PrivateKey(\"%s\") failed", key->data);
EVP_PKEY_free(pkey);
return NGX_ERROR;
}
@ -635,6 +730,197 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
}
ngx_int_t
ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_uint_t enable)
{
if (!enable) {
return NGX_OK;
}
#ifdef SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
if (SSL_CTX_compress_certs(ssl->ctx, 0) == 0) {
ngx_ssl_error(NGX_LOG_WARN, ssl->log, 0,
"SSL_CTX_compress_certs() failed, ignored");
return NGX_OK;
}
SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION);
#elif (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
if (SSL_CTX_add_cert_compression_alg(ssl->ctx, TLSEXT_cert_compression_zlib,
ngx_ssl_cert_compression_callback,
NULL)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_add_cert_compression_alg() failed");
return NGX_ERROR;
}
#else
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_certificate_compression\" is not supported "
"on this platform, ignored");
#endif
return NGX_OK;
}
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
static int
ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn, CBB *out,
const uint8_t *in, size_t in_len)
{
int rc;
X509 *cert;
u_char *p;
z_stream zstream;
ngx_str_t *comp, tmp;
ngx_pool_t *pool;
ngx_connection_t *c;
#ifdef OPENSSL_IS_BORINGSSL
{
SSL_CTX *ssl_ctx;
ngx_ssl_t *ssl;
/* BoringSSL doesn't have certificate slots, we take the last set */
ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
ssl = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_index);
cert = ((X509 **) ssl->certs.elts)[ssl->certs.nelts - 1];
}
#else
/*
* AWS-LC saves leaf certificate in SSL to associate with SSL_CTX,
* see https://github.com/aws/aws-lc/commit/e1ba2b3e5
*/
cert = SSL_get_certificate(ssl_conn);
#endif
comp = X509_get_ex_data(cert, ngx_ssl_certificate_comp_index);
if (comp != NULL) {
return CBB_add_bytes(out, comp->data, comp->len);
}
c = ngx_ssl_get_connection(ssl_conn);
pool = ngx_create_pool(256, c->log);
if (pool == NULL) {
return 0;
}
pool->log = c->log;
ngx_memzero(&zstream, sizeof(z_stream));
zstream.zalloc = ngx_ssl_cert_compression_alloc;
zstream.zfree = ngx_ssl_cert_compression_free;
zstream.opaque = pool;
rc = deflateInit(&zstream, Z_DEFAULT_COMPRESSION);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateInit() failed: %d", rc);
goto error;
}
tmp.len = deflateBound(&zstream, in_len);
tmp.data = ngx_palloc(pool, tmp.len);
if (tmp.data == NULL) {
goto error;
}
zstream.next_in = (u_char *) in;
zstream.avail_in = in_len;
zstream.next_out = tmp.data;
zstream.avail_out = tmp.len;
rc = deflate(&zstream, Z_FINISH);
if (rc != Z_STREAM_END) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"deflate(Z_FINISH) failed: %d", rc);
goto error;
}
tmp.len -= zstream.avail_out;
rc = deflateEnd(&zstream);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateEnd() failed: %d", rc);
goto error;
}
p = ngx_alloc(sizeof(ngx_str_t) + tmp.len, c->log);
if (p == NULL) {
goto error;
}
comp = (ngx_str_t *) p;
comp->len = tmp.len;
comp->data = p + sizeof(ngx_str_t);
ngx_memcpy(comp->data, tmp.data, tmp.len);
if (X509_set_ex_data(cert, ngx_ssl_certificate_comp_index, p) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_set_ex_data() failed");
ngx_free(p);
}
rc = CBB_add_bytes(out, tmp.data, tmp.len);
ngx_destroy_pool(pool);
return rc;
error:
ngx_destroy_pool(pool);
return 0;
}
static void *
ngx_ssl_cert_compression_alloc(void *opaque, u_int items, u_int size)
{
ngx_pool_t *pool = opaque;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pool->log, 0,
"cert compression alloc: n:%ud s:%ud", items, size);
return ngx_palloc(pool, items * size);
}
static void
ngx_ssl_cert_compression_free(void *opaque, void *address)
{
#if 0
ngx_pool_t *pool = opaque;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pool->log, 0,
"cert compression free: %p", address);
#endif
}
#endif
ngx_int_t
ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
ngx_uint_t prefer_server_ciphers)
@ -1289,6 +1575,8 @@ ngx_ssl_passwords_cleanup(void *data)
ngx_int_t
ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
{
#ifndef OPENSSL_NO_DH
BIO *bio;
if (file->len == 0) {
@ -1348,7 +1636,7 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
if (SSL_CTX_set0_tmp_dh_pkey(ssl->ctx, dh) != 1) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set0_tmp_dh_pkey(\"%s\") failed", file->data);
#if (OPENSSL_VERSION_NUMBER >= 0x3000001fL)
#if (OPENSSL_VERSION_NUMBER >= 0x30000010L)
EVP_PKEY_free(dh);
#endif
BIO_free(bio);
@ -1359,6 +1647,8 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
BIO_free(bio);
#endif
return NGX_OK;
}
@ -1582,6 +1872,118 @@ ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
}
void
ngx_ssl_set_client_hello_callback(SSL_CTX *ssl_ctx,
ngx_ssl_client_hello_arg *cb)
{
#ifdef SSL_CLIENT_HELLO_SUCCESS
SSL_CTX_set_client_hello_cb(ssl_ctx, ngx_ssl_client_hello_callback, NULL);
SSL_CTX_set_ex_data(ssl_ctx, ngx_ssl_client_hello_arg_index, cb);
#elif defined OPENSSL_IS_BORINGSSL
SSL_CTX_set_select_certificate_cb(ssl_ctx, ngx_ssl_select_certificate);
SSL_CTX_set_ex_data(ssl_ctx, ngx_ssl_client_hello_arg_index, cb);
#endif
}
#ifdef SSL_CLIENT_HELLO_SUCCESS
int
ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
{
u_char *p;
size_t len;
ngx_int_t rc;
ngx_str_t host;
ngx_connection_t *c;
ngx_ssl_client_hello_arg *cb;
c = ngx_ssl_get_connection(ssl_conn);
cb = SSL_CTX_get_ex_data(c->ssl->session_ctx,
ngx_ssl_client_hello_arg_index);
if (SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name,
(const unsigned char **) &p, &len)
== 0)
{
ngx_str_null(&host);
goto done;
}
/*
* RFC 6066 mandates non-zero HostName length, we follow OpenSSL.
* No more than one ServerName is expected.
*/
if (len < 5
|| (size_t) (p[0] << 8) + p[1] + 2 != len
|| p[2] != TLSEXT_NAMETYPE_host_name
|| (size_t) (p[3] << 8) + p[4] + 2 + 3 != len)
{
*ad = SSL_AD_DECODE_ERROR;
return SSL_CLIENT_HELLO_ERROR;
}
len -= 5;
p += 5;
if (len > TLSEXT_MAXLEN_host_name || ngx_strlchr(p, p + len, '\0')) {
*ad = SSL_AD_UNRECOGNIZED_NAME;
return SSL_CLIENT_HELLO_ERROR;
}
host.len = len;
host.data = p;
done:
rc = cb->servername(ssl_conn, ad, &host);
if (rc == SSL_TLSEXT_ERR_ALERT_FATAL) {
return SSL_CLIENT_HELLO_ERROR;
}
return SSL_CLIENT_HELLO_SUCCESS;
}
#elif defined OPENSSL_IS_BORINGSSL
enum ssl_select_cert_result_t ngx_ssl_select_certificate(
const SSL_CLIENT_HELLO *client_hello)
{
int ad;
ngx_int_t rc;
ngx_ssl_conn_t *ssl_conn;
ngx_connection_t *c;
ngx_ssl_client_hello_arg *cb;
ssl_conn = client_hello->ssl;
c = ngx_ssl_get_connection(ssl_conn);
cb = SSL_CTX_get_ex_data(c->ssl->session_ctx,
ngx_ssl_client_hello_arg_index);
/*
* BoringSSL sends a hardcoded "handshake_failure" alert on errors,
* we use it to map SSL_AD_INTERNAL_ERROR. To preserve other alert
* values, error handling is postponed to the servername callback.
*/
rc = cb->servername(ssl_conn, &ad, NULL);
if (rc == SSL_TLSEXT_ERR_ALERT_FATAL && ad == SSL_AD_INTERNAL_ERROR) {
return ssl_select_cert_error;
}
return ssl_select_cert_success;
}
#endif
ngx_int_t
ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
{
@ -3271,7 +3673,7 @@ ngx_ssl_shutdown_handler(ngx_event_t *ev)
}
static void
void
ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
char *text)
{
@ -3866,7 +4268,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
ngx_slab_pool_t *shpool;
ngx_ssl_sess_id_t *sess_id;
ngx_ssl_session_cache_t *cache;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
#ifdef TLS1_3_VERSION
@ -3893,7 +4294,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
return 0;
}
p = buf;
p = ngx_ssl_session_buffer;
i2d_SSL_SESSION(sess, &p);
session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);
@ -3957,7 +4358,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
#endif
ngx_memcpy(sess_id->session, buf, len);
ngx_memcpy(sess_id->session, ngx_ssl_session_buffer, len);
ngx_memcpy(sess_id->id, session_id, session_id_length);
hash = ngx_crc32_short(session_id, session_id_length);
@ -4011,12 +4412,11 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
const u_char *p;
ngx_shm_zone_t *shm_zone;
ngx_slab_pool_t *shpool;
ngx_connection_t *c;
ngx_rbtree_node_t *node, *sentinel;
ngx_ssl_session_t *sess;
ngx_ssl_sess_id_t *sess_id;
ngx_ssl_session_cache_t *cache;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
ngx_connection_t *c;
hash = ngx_crc32_short((u_char *) (uintptr_t) id, (size_t) len);
*copy = 0;
@ -4064,11 +4464,11 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
if (sess_id->expire > ngx_time()) {
slen = sess_id->len;
ngx_memcpy(buf, sess_id->session, slen);
ngx_memcpy(ngx_ssl_session_buffer, sess_id->session, slen);
ngx_shmtx_unlock(&shpool->mutex);
p = buf;
p = ngx_ssl_session_buffer;
sess = d2i_SSL_SESSION(NULL, &p, slen);
return sess;
@ -4731,10 +5131,19 @@ ngx_ssl_cleanup_ctx(void *data)
ngx_ssl_t *ssl = data;
X509 *cert;
u_char *p;
ngx_uint_t i;
for (i = 0; i < ssl->certs.nelts; i++) {
cert = ((X509 **) ssl->certs.elts)[i];
p = X509_get_ex_data(cert, ngx_ssl_certificate_comp_index);
if (p) {
ngx_free(p);
X509_set_ex_data(cert, ngx_ssl_certificate_comp_index, NULL);
}
X509_free(cert);
}
@ -5017,6 +5426,7 @@ ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
#ifdef SSL_get_negotiated_group
int nid;
const char *name;
nid = SSL_get_negotiated_group(c->ssl->connection);
@ -5028,14 +5438,20 @@ ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
return NGX_OK;
}
s->len = sizeof("0x0000") - 1;
name = SSL_group_to_name(c->ssl->connection, nid);
s->len = name ? ngx_strlen(name) : sizeof("0x0000") - 1;
s->data = ngx_pnalloc(pool, s->len);
if (s->data == NULL) {
return NGX_ERROR;
}
if (name) {
ngx_memcpy(s->data, name, s->len);
} else {
ngx_sprintf(s->data, "0x%04xd", nid & 0xffff);
}
return NGX_OK;
}
@ -5055,6 +5471,7 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
int *curves, n, i, nid;
u_char *p;
size_t len;
const char *name;
n = SSL_get1_curves(c->ssl->connection, NULL);
@ -5075,7 +5492,9 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
nid = curves[i];
if (nid & TLSEXT_nid_unknown) {
len += sizeof("0x0000") - 1;
name = SSL_group_to_name(c->ssl->connection, nid);
len += name ? ngx_strlen(name) : sizeof("0x0000") - 1;
} else {
len += ngx_strlen(OBJ_nid2sn(nid));
@ -5095,7 +5514,10 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
nid = curves[i];
if (nid & TLSEXT_nid_unknown) {
p = ngx_sprintf(p, "0x%04xd", nid & 0xffff);
name = SSL_group_to_name(c->ssl->connection, nid);
p = name ? ngx_cpymem(p, name, ngx_strlen(name))
: ngx_sprintf(p, "0x%04xd", nid & 0xffff);
} else {
p = ngx_sprintf(p, "%s", OBJ_nid2sn(nid));

View File

@ -19,19 +19,13 @@
#include <openssl/bn.h>
#include <openssl/conf.h>
#include <openssl/crypto.h>
#ifndef OPENSSL_NO_DH
#include <openssl/dh.h>
#endif
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif
#include <openssl/evp.h>
#if (NGX_QUIC)
#ifdef OPENSSL_IS_BORINGSSL
#include <openssl/hkdf.h>
#include <openssl/chacha.h>
#else
#include <openssl/kdf.h>
#endif
#endif
#include <openssl/hmac.h>
#ifndef OPENSSL_NO_OCSP
#include <openssl/ocsp.h>
@ -83,6 +77,22 @@
#endif
#ifdef OPENSSL_NO_DEPRECATED_3_4
#define SSL_SESSION_get_time(s) SSL_SESSION_get_time_ex(s)
#define SSL_SESSION_set_time(s, t) SSL_SESSION_set_time_ex(s, t)
#endif
#ifdef OPENSSL_NO_DEPRECATED_3_0
#define EVP_CIPHER_CTX_cipher(c) EVP_CIPHER_CTX_get0_cipher(c)
#endif
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
#define SSL_group_to_name(s, nid) NULL
#endif
typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t;
@ -133,6 +143,7 @@ struct ngx_ssl_connection_s {
unsigned in_ocsp:1;
unsigned early_preread:1;
unsigned write_blocked:1;
unsigned sni_accepted:1;
};
@ -142,7 +153,7 @@ struct ngx_ssl_connection_s {
#define NGX_SSL_DFLT_BUILTIN_SCACHE -5
#define NGX_SSL_MAX_SESSION_SIZE 4096
#define NGX_SSL_MAX_SESSION_SIZE 8192
typedef struct ngx_ssl_sess_id_s ngx_ssl_sess_id_t;
@ -179,6 +190,13 @@ typedef struct {
} ngx_ssl_session_cache_t;
typedef int (*ngx_ssl_servername_pt)(ngx_ssl_conn_t *, int *, void *);
typedef struct {
ngx_ssl_servername_pt servername;
} ngx_ssl_client_hello_arg;
#define NGX_SSL_SSLv2 0x0002
#define NGX_SSL_SSLv3 0x0004
#define NGX_SSL_TLSv1 0x0008
@ -205,6 +223,8 @@ typedef struct {
#define NGX_SSL_CACHE_CRL 2
#define NGX_SSL_CACHE_CA 3
#define NGX_SSL_CACHE_INVALIDATE 0x80000000
ngx_int_t ngx_ssl_init(ngx_log_t *log);
ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);
@ -214,7 +234,10 @@ ngx_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
ngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
ngx_str_t *cert, ngx_str_t *key, ngx_ssl_cache_t *cache,
ngx_array_t *passwords);
ngx_int_t ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_uint_t enable);
ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
ngx_uint_t prefer_server_ciphers);
@ -237,10 +260,12 @@ ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s);
void ngx_ssl_ocsp_cleanup(ngx_connection_t *c);
ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data);
ngx_ssl_cache_t *ngx_ssl_cache_init(ngx_pool_t *pool, ngx_uint_t max,
time_t valid, time_t inactive);
void *ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
ngx_str_t *path, void *data);
void *ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index,
char **err, ngx_str_t *path, void *data);
void *ngx_ssl_cache_connection_fetch(ngx_ssl_cache_t *cache, ngx_pool_t *pool,
ngx_uint_t index, char **err, ngx_str_t *path, void *data);
ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf,
@ -261,6 +286,15 @@ ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_array_t *paths);
ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
void ngx_ssl_set_client_hello_callback(SSL_CTX *ssl_ctx,
ngx_ssl_client_hello_arg *cb);
#ifdef SSL_CLIENT_HELLO_SUCCESS
int ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);
#elif defined OPENSSL_IS_BORINGSSL
enum ssl_select_cert_result_t ngx_ssl_select_certificate(
const SSL_CLIENT_HELLO *client_hello);
#endif
ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
ngx_uint_t flags);
@ -343,6 +377,8 @@ ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
void ngx_ssl_free_buffer(ngx_connection_t *c);
ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);
void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
char *text);
void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
char *fmt, ...);
void ngx_ssl_cleanup_ctx(void *data);
@ -355,6 +391,11 @@ extern int ngx_ssl_ticket_keys_index;
extern int ngx_ssl_ocsp_index;
extern int ngx_ssl_index;
extern int ngx_ssl_certificate_name_index;
extern int ngx_ssl_certificate_comp_index;
extern int ngx_ssl_client_hello_arg_index;
extern u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE];
#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */

View File

@ -8,10 +8,26 @@
#include <ngx_core.h>
#include <ngx_event.h>
#ifdef ERR_R_OSSL_STORE_LIB
#include <openssl/store.h>
#include <openssl/ui.h>
#endif
#define NGX_SSL_CACHE_PATH 0
#define NGX_SSL_CACHE_DATA 1
#define NGX_SSL_CACHE_ENGINE 2
#define NGX_SSL_CACHE_STORE 3
#define NGX_SSL_CACHE_DISABLED (ngx_array_t *) (uintptr_t) -1
#define ngx_ssl_cache_get_conf(cycle) \
(ngx_ssl_cache_t *) ngx_get_conf(cycle->conf_ctx, ngx_openssl_cache_module)
#define ngx_ssl_cache_get_old_conf(cycle) \
cycle->old_cycle->conf_ctx ? ngx_ssl_cache_get_conf(cycle->old_cycle) \
: NULL
typedef struct {
@ -36,22 +52,45 @@ typedef struct {
typedef struct {
ngx_rbtree_node_t node;
ngx_queue_t queue;
ngx_ssl_cache_key_t id;
ngx_ssl_cache_type_t *type;
void *value;
time_t created;
time_t accessed;
time_t mtime;
ngx_file_uniq_t uniq;
} ngx_ssl_cache_node_t;
typedef struct {
struct ngx_ssl_cache_s {
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
} ngx_ssl_cache_t;
ngx_queue_t expire_queue;
ngx_flag_t inheritable;
ngx_uint_t current;
ngx_uint_t max;
time_t valid;
time_t inactive;
};
typedef struct {
ngx_str_t *pwd;
unsigned encrypted:1;
} ngx_ssl_cache_pwd_t;
static ngx_int_t ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index,
ngx_str_t *path, ngx_ssl_cache_key_t *id);
static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache,
ngx_ssl_cache_type_t *type, ngx_ssl_cache_key_t *id, uint32_t hash);
static void ngx_ssl_cache_expire(ngx_ssl_cache_t *cache, ngx_uint_t n,
ngx_log_t *log);
static void *ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err,
void *data);
@ -76,26 +115,44 @@ static void *ngx_ssl_cache_ca_create(ngx_ssl_cache_key_t *id, char **err,
static BIO *ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err);
static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle);
static char *ngx_openssl_cache_init_conf(ngx_cycle_t *cycle, void *conf);
static void ngx_ssl_cache_cleanup(void *data);
static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
static void ngx_ssl_cache_node_free(ngx_rbtree_t *rbtree,
ngx_ssl_cache_node_t *cn);
static ngx_int_t ngx_openssl_cache_init_worker(ngx_cycle_t *cycle);
static ngx_command_t ngx_openssl_cache_commands[] = {
{ ngx_string("ssl_object_cache_inheritable"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_ssl_cache_t, inheritable),
NULL },
ngx_null_command
};
static ngx_core_module_t ngx_openssl_cache_module_ctx = {
ngx_string("openssl_cache"),
ngx_openssl_cache_create_conf,
NULL
ngx_openssl_cache_init_conf
};
ngx_module_t ngx_openssl_cache_module = {
NGX_MODULE_V1,
&ngx_openssl_cache_module_ctx, /* module context */
NULL, /* module directives */
ngx_openssl_cache_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
ngx_openssl_cache_init_worker, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
@ -132,18 +189,31 @@ void *
ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
ngx_str_t *path, void *data)
{
void *value;
time_t mtime;
uint32_t hash;
ngx_ssl_cache_t *cache;
ngx_int_t rc;
ngx_uint_t invalidate;
ngx_file_uniq_t uniq;
ngx_file_info_t fi;
ngx_ssl_cache_t *cache, *old_cache;
ngx_ssl_cache_key_t id;
ngx_ssl_cache_type_t *type;
ngx_ssl_cache_node_t *cn;
*err = NULL;
invalidate = index & NGX_SSL_CACHE_INVALIDATE;
index &= ~NGX_SSL_CACHE_INVALIDATE;
if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) {
return NULL;
}
if (id.type == NGX_SSL_CACHE_DATA) {
invalidate = 0;
}
cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx,
ngx_openssl_cache_module);
@ -151,12 +221,66 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
hash = ngx_murmur_hash2(id.data, id.len);
cn = ngx_ssl_cache_lookup(cache, type, &id, hash);
if (cn != NULL) {
if (!invalidate) {
return type->ref(err, cn->value);
}
type->free(cn->value);
ngx_rbtree_delete(&cache->rbtree, &cn->node);
}
value = NULL;
if (id.type == NGX_SSL_CACHE_PATH
&& (rc = ngx_file_info(id.data, &fi)) != NGX_FILE_ERROR)
{
mtime = ngx_file_mtime(&fi);
uniq = ngx_file_uniq(&fi);
} else {
rc = NGX_FILE_ERROR;
mtime = 0;
uniq = 0;
}
/* try to use a reference from the old cycle */
old_cache = ngx_ssl_cache_get_old_conf(cf->cycle);
if (old_cache && old_cache->inheritable && !invalidate) {
cn = ngx_ssl_cache_lookup(old_cache, type, &id, hash);
if (cn != NULL) {
switch (id.type) {
case NGX_SSL_CACHE_DATA:
value = type->ref(err, cn->value);
break;
default:
if (rc != NGX_FILE_ERROR
&& uniq == cn->uniq && mtime == cn->mtime)
{
value = type->ref(err, cn->value);
}
break;
}
}
}
if (value == NULL) {
value = type->create(&id, err, &data);
if (value == NULL || data == NGX_SSL_CACHE_DISABLED) {
return value;
}
}
cn = ngx_palloc(cf->pool, sizeof(ngx_ssl_cache_node_t) + id.len + 1);
if (cn == NULL) {
type->free(value);
return NULL;
}
@ -165,13 +289,13 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
cn->id.len = id.len;
cn->id.type = id.type;
cn->type = type;
cn->value = value;
cn->mtime = mtime;
cn->uniq = uniq;
ngx_cpystrn(cn->id.data, id.data, id.len + 1);
cn->value = type->create(&id, err, data);
if (cn->value == NULL) {
return NULL;
}
ngx_queue_init(&cn->queue);
ngx_rbtree_insert(&cache->rbtree, &cn->node);
@ -180,18 +304,150 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
void *
ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index, char **err,
ngx_str_t *path, void *data)
ngx_ssl_cache_connection_fetch(ngx_ssl_cache_t *cache, ngx_pool_t *pool,
ngx_uint_t index, char **err, ngx_str_t *path, void *data)
{
void *value;
time_t now;
uint32_t hash;
ngx_uint_t invalidate;
ngx_file_info_t fi;
ngx_ssl_cache_key_t id;
ngx_ssl_cache_type_t *type;
ngx_ssl_cache_node_t *cn;
*err = NULL;
invalidate = index & NGX_SSL_CACHE_INVALIDATE;
index &= ~NGX_SSL_CACHE_INVALIDATE;
if (ngx_ssl_cache_init_key(pool, index, path, &id) != NGX_OK) {
return NULL;
}
return ngx_ssl_cache_types[index].create(&id, err, data);
type = &ngx_ssl_cache_types[index];
if (cache == NULL) {
return type->create(&id, err, &data);
}
now = ngx_time();
hash = ngx_murmur_hash2(id.data, id.len);
cn = ngx_ssl_cache_lookup(cache, type, &id, hash);
if (cn != NULL) {
ngx_queue_remove(&cn->queue);
if (id.type == NGX_SSL_CACHE_DATA) {
goto found;
}
if (!invalidate && now - cn->created <= cache->valid) {
goto found;
}
switch (id.type) {
case NGX_SSL_CACHE_PATH:
if (ngx_file_info(id.data, &fi) != NGX_FILE_ERROR) {
if (!invalidate
&& ngx_file_uniq(&fi) == cn->uniq
&& ngx_file_mtime(&fi) == cn->mtime)
{
break;
}
cn->mtime = ngx_file_mtime(&fi);
cn->uniq = ngx_file_uniq(&fi);
} else {
cn->mtime = 0;
cn->uniq = 0;
}
/* fall through */
default:
ngx_log_debug1(NGX_LOG_DEBUG_CORE, pool->log, 0,
"update cached ssl object: %s", cn->id.data);
type->free(cn->value);
value = type->create(&id, err, &data);
if (value == NULL || data == NGX_SSL_CACHE_DISABLED) {
ngx_rbtree_delete(&cache->rbtree, &cn->node);
cache->current--;
ngx_free(cn);
return value;
}
cn->value = value;
}
cn->created = now;
goto found;
}
value = type->create(&id, err, &data);
if (value == NULL || data == NGX_SSL_CACHE_DISABLED) {
return value;
}
cn = ngx_alloc(sizeof(ngx_ssl_cache_node_t) + id.len + 1, pool->log);
if (cn == NULL) {
type->free(value);
return NULL;
}
cn->node.key = hash;
cn->id.data = (u_char *)(cn + 1);
cn->id.len = id.len;
cn->id.type = id.type;
cn->type = type;
cn->value = value;
cn->created = now;
ngx_cpystrn(cn->id.data, id.data, id.len + 1);
if (id.type == NGX_SSL_CACHE_PATH) {
if (ngx_file_info(id.data, &fi) != NGX_FILE_ERROR) {
cn->mtime = ngx_file_mtime(&fi);
cn->uniq = ngx_file_uniq(&fi);
} else {
cn->mtime = 0;
cn->uniq = 0;
}
}
ngx_ssl_cache_expire(cache, 1, pool->log);
if (cache->current >= cache->max) {
ngx_ssl_cache_expire(cache, 0, pool->log);
}
ngx_rbtree_insert(&cache->rbtree, &cn->node);
cache->current++;
found:
cn->accessed = now;
ngx_queue_insert_head(&cache->expire_queue, &cn->queue);
return type->ref(err, cn->value);
}
@ -209,6 +465,11 @@ ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path,
{
id->type = NGX_SSL_CACHE_ENGINE;
} else if (index == NGX_SSL_CACHE_PKEY
&& ngx_strncmp(path->data, "store:", sizeof("store:") - 1) == 0)
{
id->type = NGX_SSL_CACHE_STORE;
} else {
if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path)
!= NGX_OK)
@ -278,6 +539,37 @@ ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type,
}
static void
ngx_ssl_cache_expire(ngx_ssl_cache_t *cache, ngx_uint_t n,
ngx_log_t *log)
{
time_t now;
ngx_queue_t *q;
ngx_ssl_cache_node_t *cn;
now = ngx_time();
while (n < 3) {
if (ngx_queue_empty(&cache->expire_queue)) {
return;
}
q = ngx_queue_last(&cache->expire_queue);
cn = ngx_queue_data(q, ngx_ssl_cache_node_t, queue);
if (n++ != 0 && now - cn->accessed <= cache->inactive) {
return;
}
ngx_ssl_cache_node_free(&cache->rbtree, cn);
cache->current--;
}
}
static void *
ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err, void *data)
{
@ -394,13 +686,13 @@ ngx_ssl_cache_cert_ref(char **err, void *data)
static void *
ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
{
ngx_array_t *passwords = data;
ngx_array_t **passwords = data;
BIO *bio;
EVP_PKEY *pkey;
ngx_str_t *pwd;
ngx_uint_t tries;
pem_password_cb *cb;
ngx_ssl_cache_pwd_t cb_data, *pwd;
if (id->type == NGX_SSL_CACHE_ENGINE) {
@ -428,7 +720,7 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
return NULL;
}
pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
pkey = ENGINE_load_private_key(engine, (char *) last, NULL, NULL);
if (pkey == NULL) {
*err = "ENGINE_load_private_key() failed";
@ -448,22 +740,91 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
#endif
}
bio = ngx_ssl_cache_create_bio(id, err);
if (bio == NULL) {
return NULL;
}
cb_data.encrypted = 0;
if (passwords) {
tries = passwords->nelts;
pwd = passwords->elts;
if (*passwords) {
cb_data.pwd = (*passwords)->elts;
tries = (*passwords)->nelts;
pwd = &cb_data;
cb = ngx_ssl_cache_pkey_password_callback;
} else {
cb_data.pwd = NULL;
tries = 1;
pwd = NULL;
cb = NULL;
}
if (id->type == NGX_SSL_CACHE_STORE) {
#ifdef ERR_R_OSSL_STORE_LIB
u_char *uri;
UI_METHOD *method;
OSSL_STORE_CTX *store;
OSSL_STORE_INFO *info;
method = (cb != NULL) ? UI_UTIL_wrap_read_pem_callback(cb, 0) : NULL;
uri = id->data + sizeof("store:") - 1;
store = OSSL_STORE_open((char *) uri, method, pwd, NULL, NULL);
if (store == NULL) {
*err = "OSSL_STORE_open() failed";
if (method != NULL) {
UI_destroy_method(method);
}
return NULL;
}
pkey = NULL;
while (pkey == NULL && !OSSL_STORE_eof(store)) {
info = OSSL_STORE_load(store);
if (info == NULL) {
continue;
}
if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY) {
pkey = OSSL_STORE_INFO_get1_PKEY(info);
}
OSSL_STORE_INFO_free(info);
}
OSSL_STORE_close(store);
if (method != NULL) {
UI_destroy_method(method);
}
if (pkey == NULL) {
*err = "OSSL_STORE_load() failed";
return NULL;
}
if (cb_data.encrypted) {
*passwords = NGX_SSL_CACHE_DISABLED;
}
return pkey;
#else
*err = "loading \"store:...\" certificate keys is not supported";
return NULL;
#endif
}
bio = ngx_ssl_cache_create_bio(id, err);
if (bio == NULL) {
return NULL;
}
for ( ;; ) {
pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
@ -474,7 +835,7 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
if (tries-- > 1) {
ERR_clear_error();
(void) BIO_reset(bio);
pwd++;
cb_data.pwd++;
continue;
}
@ -483,6 +844,10 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
return NULL;
}
if (cb_data.encrypted) {
*passwords = NGX_SSL_CACHE_DISABLED;
}
BIO_free(bio);
return pkey;
@ -493,7 +858,9 @@ static int
ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag,
void *userdata)
{
ngx_str_t *pwd = userdata;
ngx_ssl_cache_pwd_t *data = userdata;
ngx_str_t *pwd;
if (rwflag) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
@ -502,6 +869,10 @@ ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag,
return 0;
}
data->encrypted = 1;
pwd = data->pwd;
if (pwd == NULL) {
return 0;
}
@ -722,14 +1093,51 @@ static void *
ngx_openssl_cache_create_conf(ngx_cycle_t *cycle)
{
ngx_ssl_cache_t *cache;
ngx_pool_cleanup_t *cln;
cache = ngx_pcalloc(cycle->pool, sizeof(ngx_ssl_cache_t));
cache = ngx_ssl_cache_init(cycle->pool, 0, 0, 0);
if (cache == NULL) {
return NULL;
}
cln = ngx_pool_cleanup_add(cycle->pool, 0);
cache->inheritable = NGX_CONF_UNSET;
return cache;
}
static char *
ngx_openssl_cache_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_ssl_cache_t *cache = conf;
ngx_conf_init_value(cache->inheritable, 1);
return NGX_CONF_OK;
}
ngx_ssl_cache_t *
ngx_ssl_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t valid,
time_t inactive)
{
ngx_ssl_cache_t *cache;
ngx_pool_cleanup_t *cln;
cache = ngx_pcalloc(pool, sizeof(ngx_ssl_cache_t));
if (cache == NULL) {
return NULL;
}
ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
ngx_ssl_cache_node_insert);
ngx_queue_init(&cache->expire_queue);
cache->max = max;
cache->valid = valid;
cache->inactive = inactive;
cln = ngx_pool_cleanup_add(pool, 0);
if (cln == NULL) {
return NULL;
}
@ -737,9 +1145,6 @@ ngx_openssl_cache_create_conf(ngx_cycle_t *cycle)
cln->handler = ngx_ssl_cache_cleanup;
cln->data = cache;
ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
ngx_ssl_cache_node_insert);
return cache;
}
@ -759,12 +1164,47 @@ ngx_ssl_cache_cleanup(void *data)
return;
}
for (node = ngx_rbtree_min(tree->root, tree->sentinel);
node;
node = ngx_rbtree_next(tree, node))
{
node = ngx_rbtree_min(tree->root, tree->sentinel);
while (node != NULL) {
cn = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node);
node = ngx_rbtree_next(tree, node);
ngx_ssl_cache_node_free(tree, cn);
if (cache->max) {
cache->current--;
}
}
if (cache->current) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
"%ui items still left in ssl cache",
cache->current);
}
if (!ngx_queue_empty(&cache->expire_queue)) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
"queue still is not empty in ssl cache");
}
}
static void
ngx_ssl_cache_node_free(ngx_rbtree_t *rbtree, ngx_ssl_cache_node_t *cn)
{
cn->type->free(cn->value);
ngx_rbtree_delete(rbtree, &cn->node);
if (!ngx_queue_empty(&cn->queue)) {
ngx_queue_remove(&cn->queue);
ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
"delete cached ssl object: %s", cn->id.data);
ngx_free(cn);
}
}
@ -808,3 +1248,20 @@ ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
node->right = sentinel;
ngx_rbt_red(node);
}
static ngx_int_t
ngx_openssl_cache_init_worker(ngx_cycle_t *cycle)
{
#ifdef ERR_R_OSSL_STORE_LIB
if (ngx_process != NGX_PROCESS_WORKER) {
return NGX_OK;
}
UI_set_default_method(UI_null());
#endif
return NGX_OK;
}

View File

@ -72,7 +72,7 @@ ngx_quic_connstate_dbg(ngx_connection_t *c)
if (qc) {
if (qc->error != (ngx_uint_t) -1) {
if (qc->error) {
p = ngx_slprintf(p, last, "%s", qc->error_app ? " app" : "");
p = ngx_slprintf(p, last, " error:%ui", qc->error);
@ -135,6 +135,9 @@ ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp)
if (scid.len != ctp->initial_scid.len
|| ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0)
{
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
qc->error_reason = "invalid initial_source_connection_id";
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic client initial_source_connection_id mismatch");
return NGX_ERROR;
@ -257,9 +260,9 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
qc->send_ctx[i].pending_ack = NGX_QUIC_UNSET_PN;
}
qc->send_ctx[0].level = ssl_encryption_initial;
qc->send_ctx[1].level = ssl_encryption_handshake;
qc->send_ctx[2].level = ssl_encryption_application;
qc->send_ctx[0].level = NGX_QUIC_ENCRYPTION_INITIAL;
qc->send_ctx[1].level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
qc->send_ctx[2].level = NGX_QUIC_ENCRYPTION_APPLICATION;
ngx_queue_init(&qc->free_frames);
@ -308,11 +311,16 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
qc->streams.client_max_streams_uni = qc->tp.initial_max_streams_uni;
qc->streams.client_max_streams_bidi = qc->tp.initial_max_streams_bidi;
qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
ngx_max(2 * qc->tp.max_udp_payload_size,
qc->congestion.window = ngx_min(10 * NGX_QUIC_MIN_INITIAL_SIZE,
ngx_max(2 * NGX_QUIC_MIN_INITIAL_SIZE,
14720));
qc->congestion.ssthresh = (size_t) -1;
qc->congestion.recovery_start = ngx_current_msec;
qc->congestion.mtu = NGX_QUIC_MIN_INITIAL_SIZE;
qc->congestion.recovery_start = ngx_current_msec - 1;
qc->max_frames = (conf->max_concurrent_streams_uni
+ conf->max_concurrent_streams_bidi)
* conf->stream_buffer_size / 2000;
if (pkt->validated && pkt->retried) {
qc->tp.retry_scid.len = pkt->dcid.len;
@ -512,7 +520,7 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc)
* to terminate the connection immediately.
*/
if (qc->error == (ngx_uint_t) -1) {
if (qc->error == 0 && rc == NGX_ERROR) {
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
qc->error_app = 0;
}
@ -792,13 +800,13 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
pkt->dcid.len, &pkt->dcid);
#if (NGX_DEBUG)
if (pkt->level != ssl_encryption_application) {
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic packet rx scid len:%uz %xV",
pkt->scid.len, &pkt->scid);
}
if (pkt->level == ssl_encryption_initial) {
if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic address validation token len:%uz %xV",
pkt->token.len, &pkt->token);
@ -815,7 +823,7 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
return NGX_DECLINED;
}
if (pkt->level != ssl_encryption_application) {
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
if (pkt->version != qc->version) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
@ -845,7 +853,9 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
rc = ngx_quic_handle_payload(c, pkt);
if (rc == NGX_DECLINED && pkt->level == ssl_encryption_application) {
if (rc == NGX_DECLINED
&& pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION)
{
if (ngx_quic_handle_stateless_reset(c, pkt) == NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic stateless reset packet detected");
@ -866,11 +876,11 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
return ngx_quic_negotiate_version(c, pkt);
}
if (pkt->level == ssl_encryption_application) {
if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
return ngx_quic_send_stateless_reset(c, conf, pkt);
}
if (pkt->level != ssl_encryption_initial) {
if (pkt->level != NGX_QUIC_ENCRYPTION_INITIAL) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic expected initial, got handshake");
return NGX_ERROR;
@ -953,8 +963,8 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
qc = ngx_quic_get_connection(c);
qc->error = (ngx_uint_t) -1;
qc->error_reason = 0;
qc->error = 0;
qc->error_reason = NULL;
c->log->action = "decrypting packet";
@ -965,10 +975,10 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
return NGX_DECLINED;
}
#if !defined (OPENSSL_IS_BORINGSSL)
/* OpenSSL provides read keys for an application level before it's ready */
#if (NGX_QUIC_QUICTLS_API)
/* QuicTLS provides app read keys before completing handshake */
if (pkt->level == ssl_encryption_application && !c->ssl->handshaked) {
if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION && !c->ssl->handshaked) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic no %s keys ready, ignoring packet",
ngx_quic_level_name(pkt->level));
@ -1006,14 +1016,14 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
}
}
if (pkt->level == ssl_encryption_handshake) {
if (pkt->level == NGX_QUIC_ENCRYPTION_HANDSHAKE) {
/*
* RFC 9001, 4.9.1. Discarding Initial Keys
*
* The successful use of Handshake packets indicates
* that no more Initial packets need to be exchanged
*/
ngx_quic_discard_ctx(c, ssl_encryption_initial);
ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_INITIAL);
if (!qc->path->validated) {
qc->path->validated = 1;
@ -1022,14 +1032,14 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
}
}
if (pkt->level == ssl_encryption_application) {
if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
/*
* RFC 9001, 4.9.3. Discarding 0-RTT Keys
*
* After receiving a 1-RTT packet, servers MUST discard
* 0-RTT keys within a short time
*/
ngx_quic_discard_ctx(c, ssl_encryption_early_data);
ngx_quic_keys_discard(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA);
}
if (qc->closing) {
@ -1056,7 +1066,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
c->log->action = "handling payload";
if (pkt->level != ssl_encryption_application) {
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
return ngx_quic_handle_frames(c, pkt);
}
@ -1081,7 +1091,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
void
ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
ngx_quic_discard_ctx(ngx_connection_t *c, ngx_uint_t level)
{
ngx_queue_t *q;
ngx_quic_frame_t *f;
@ -1122,7 +1132,7 @@ ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
ngx_quic_free_frame(c, f);
}
if (level == ssl_encryption_initial) {
if (level == NGX_QUIC_ENCRYPTION_INITIAL) {
/* close temporary listener with initial dcid */
qsock = ngx_quic_find_socket(c, NGX_QUIC_UNSET_PN);
if (qsock) {

View File

@ -12,6 +12,22 @@
#include <ngx_core.h>
#if (OPENSSL_VERSION_NUMBER >= 0x30500010L)
#define NGX_QUIC_OPENSSL_API 1
#elif (defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION)
#define NGX_QUIC_QUICTLS_API 1
#elif (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC \
|| defined LIBRESSL_VERSION_NUMBER)
#define NGX_QUIC_BORINGSSL_API 1
#else
#define NGX_QUIC_BORINGSSL_API 1
#define NGX_QUIC_OPENSSL_COMPAT 1
#endif
#define NGX_QUIC_MAX_UDP_PAYLOAD_SIZE 65527
#define NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT 3

View File

@ -20,6 +20,10 @@
/* RFC 9002, 7.6.1. Duration: kPersistentCongestionThreshold */
#define NGX_QUIC_PERSISTENT_CONGESTION_THR 3
/* CUBIC parameters x10 */
#define NGX_QUIC_CUBIC_BETA 7
#define NGX_QUIC_CUBIC_C 4
/* send time of ACK'ed packets */
typedef struct {
@ -29,18 +33,22 @@ typedef struct {
} ngx_quic_ack_stat_t;
static ngx_inline ngx_msec_t ngx_quic_lost_threshold(ngx_quic_connection_t *qc);
static ngx_inline ngx_msec_t ngx_quic_time_threshold(ngx_quic_connection_t *qc);
static uint64_t ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx);
static void ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
enum ssl_encryption_level_t level, ngx_msec_t send_time);
ngx_uint_t level, ngx_msec_t send_time);
static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max,
ngx_quic_ack_stat_t *st);
static size_t ngx_quic_congestion_cubic(ngx_connection_t *c);
static void ngx_quic_drop_ack_ranges(ngx_connection_t *c,
ngx_quic_send_ctx_t *ctx, uint64_t pn);
static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c,
ngx_quic_ack_stat_t *st);
static ngx_msec_t ngx_quic_congestion_cubic_time(ngx_connection_t *c);
static ngx_msec_t ngx_quic_pcg_duration(ngx_connection_t *c);
static void ngx_quic_persistent_congestion(ngx_connection_t *c);
static ngx_msec_t ngx_quic_oldest_sent_packet(ngx_connection_t *c);
static void ngx_quic_congestion_lost(ngx_connection_t *c,
ngx_quic_frame_t *frame);
static void ngx_quic_lost_handler(ngx_event_t *ev);
@ -48,7 +56,7 @@ static void ngx_quic_lost_handler(ngx_event_t *ev);
/* RFC 9002, 6.1.2. Time Threshold: kTimeThreshold, kGranularity */
static ngx_inline ngx_msec_t
ngx_quic_lost_threshold(ngx_quic_connection_t *qc)
ngx_quic_time_threshold(ngx_quic_connection_t *qc)
{
ngx_msec_t thr;
@ -59,6 +67,29 @@ ngx_quic_lost_threshold(ngx_quic_connection_t *qc)
}
static uint64_t
ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx)
{
uint64_t pkt_thr;
ngx_queue_t *q;
ngx_quic_frame_t *f;
if (ngx_queue_empty(&ctx->sent)) {
return NGX_QUIC_PKT_THR;
}
q = ngx_queue_head(&ctx->sent);
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
pkt_thr = (ctx->pnum - f->pnum) / 2;
if (pkt_thr <= NGX_QUIC_PKT_THR) {
return NGX_QUIC_PKT_THR;
}
return pkt_thr;
}
ngx_int_t
ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
ngx_quic_frame_t *f)
@ -77,7 +108,7 @@ ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_handle_ack_frame level:%d", pkt->level);
"quic ngx_quic_handle_ack_frame level:%ui", pkt->level);
ack = &f->u.ack;
@ -176,7 +207,7 @@ ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
static void
ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
enum ssl_encryption_level_t level, ngx_msec_t send_time)
ngx_uint_t level, ngx_msec_t send_time)
{
ngx_msec_t latest_rtt, ack_delay, adjusted_rtt, rttvar_sample;
ngx_quic_connection_t *qc;
@ -229,7 +260,7 @@ ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
qc = ngx_quic_get_connection(c);
if (ctx->level == ssl_encryption_application) {
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
if (ngx_quic_handle_path_mtu(c, qc->path, min, max) != NGX_OK) {
return NGX_ERROR;
}
@ -313,8 +344,9 @@ ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
void
ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
{
size_t w_cubic;
ngx_uint_t blocked;
ngx_msec_t timer;
ngx_msec_t now, timer;
ngx_quic_congestion_t *cg;
ngx_quic_connection_t *qc;
@ -329,16 +361,34 @@ ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
return;
}
now = ngx_current_msec;
blocked = (cg->in_flight >= cg->window) ? 1 : 0;
cg->in_flight -= f->plen;
/* prevent recovery_start from wrapping */
timer = now - cg->recovery_start;
if ((ngx_msec_int_t) timer < 0) {
cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
}
timer = f->send_time - cg->recovery_start;
if ((ngx_msec_int_t) timer <= 0) {
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion ack recovery win:%uz ss:%z if:%uz",
cg->window, cg->ssthresh, cg->in_flight);
"quic congestion ack rec t:%M win:%uz if:%uz",
now, cg->window, cg->in_flight);
goto done;
}
if (cg->idle) {
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion ack idle t:%M win:%uz if:%uz",
now, cg->window, cg->in_flight);
goto done;
}
@ -346,24 +396,51 @@ ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
if (cg->window < cg->ssthresh) {
cg->window += f->plen;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion slow start win:%uz ss:%z if:%uz",
cg->window, cg->ssthresh, cg->in_flight);
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion ack ss t:%M win:%uz ss:%z if:%uz",
now, cg->window, cg->ssthresh, cg->in_flight);
} else {
cg->window += qc->tp.max_udp_payload_size * f->plen / cg->window;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion avoidance win:%uz ss:%z if:%uz",
cg->window, cg->ssthresh, cg->in_flight);
/* RFC 9438, 4.2. Window Increase Function */
w_cubic = ngx_quic_congestion_cubic(c);
if (cg->window < cg->w_prior) {
cg->w_est += (uint64_t) cg->mtu * f->plen
* 3 * (10 - NGX_QUIC_CUBIC_BETA)
/ (10 + NGX_QUIC_CUBIC_BETA) / cg->window;
} else {
cg->w_est += (uint64_t) cg->mtu * f->plen / cg->window;
}
/* prevent recovery_start from wrapping */
if (w_cubic < cg->w_est) {
cg->window = cg->w_est;
timer = cg->recovery_start - ngx_current_msec + qc->tp.max_idle_timeout * 2;
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion ack reno t:%M win:%uz c:%uz if:%uz",
now, cg->window, w_cubic, cg->in_flight);
if ((ngx_msec_int_t) timer < 0) {
cg->recovery_start = ngx_current_msec - qc->tp.max_idle_timeout * 2;
} else if (w_cubic > cg->window) {
if (w_cubic >= cg->window * 3 / 2) {
cg->window += cg->mtu / 2;
} else {
cg->window += (uint64_t) cg->mtu * (w_cubic - cg->window)
/ cg->window;
}
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion ack cubic t:%M win:%uz c:%uz if:%uz",
now, cg->window, w_cubic, cg->in_flight);
} else {
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion ack skip t:%M win:%uz c:%uz if:%uz",
now, cg->window, w_cubic, cg->in_flight);
}
}
done:
@ -374,6 +451,87 @@ done:
}
static size_t
ngx_quic_congestion_cubic(ngx_connection_t *c)
{
int64_t w, t, cc;
ngx_msec_t now;
ngx_quic_congestion_t *cg;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
cg = &qc->congestion;
ngx_quic_congestion_idle(c, cg->idle);
now = ngx_current_msec;
t = (ngx_msec_int_t) (now - cg->k);
if (t > 1000000) {
w = NGX_MAX_SIZE_T_VALUE;
goto done;
}
if (t < -1000000) {
w = 0;
goto done;
}
/*
* RFC 9438, Figure 1
*
* w_cubic = C * (t_msec / 1000) ^ 3 * mtu + w_max
*/
cc = 10000000000ll / (int64_t) cg->mtu / NGX_QUIC_CUBIC_C;
w = t * t * t / cc + (int64_t) cg->w_max;
if (w > NGX_MAX_SIZE_T_VALUE) {
w = NGX_MAX_SIZE_T_VALUE;
}
if (w < 0) {
w = 0;
}
done:
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic cubic t:%L w:%L wm:%uz", t, w, cg->w_max);
return w;
}
void
ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle)
{
ngx_msec_t now;
ngx_quic_congestion_t *cg;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
cg = &qc->congestion;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion idle:%ui", idle);
if (cg->window >= cg->ssthresh) {
/* RFC 9438, 5.8. Behavior for Application-Limited Flows */
now = ngx_current_msec;
if (cg->idle) {
cg->k += now - cg->idle_start;
}
cg->idle_start = now;
}
cg->idle = idle;
}
static void
ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
uint64_t pn)
@ -435,6 +593,7 @@ ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
static ngx_int_t
ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
{
uint64_t pkt_thr;
ngx_uint_t i, nlost;
ngx_msec_t now, wait, thr, oldest, newest;
ngx_queue_t *q;
@ -444,11 +603,12 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
qc = ngx_quic_get_connection(c);
now = ngx_current_msec;
thr = ngx_quic_lost_threshold(qc);
thr = ngx_quic_time_threshold(qc);
/* send time of lost packets across all send contexts */
oldest = NGX_TIMER_INFINITE;
newest = NGX_TIMER_INFINITE;
#if (NGX_SUPPRESS_WARN)
oldest = now;
newest = now;
#endif
nlost = 0;
@ -460,6 +620,8 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
continue;
}
pkt_thr = ngx_quic_packet_threshold(ctx);
while (!ngx_queue_empty(&ctx->sent)) {
q = ngx_queue_head(&ctx->sent);
@ -471,23 +633,27 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
wait = start->send_time + thr - now;
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic detect_lost pnum:%uL thr:%M wait:%i level:%d",
start->pnum, thr, (ngx_int_t) wait, start->level);
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic detect_lost pnum:%uL thr:%M pthr:%uL wait:%i level:%ui",
start->pnum, thr, pkt_thr, (ngx_int_t) wait, start->level);
if ((ngx_msec_int_t) wait > 0
&& ctx->largest_ack - start->pnum < NGX_QUIC_PKT_THR)
&& ctx->largest_ack - start->pnum < pkt_thr)
{
break;
}
if (start->send_time > qc->first_rtt) {
if ((ngx_msec_int_t) (start->send_time - qc->first_rtt) > 0) {
if (oldest == NGX_TIMER_INFINITE || start->send_time < oldest) {
if (nlost == 0
|| (ngx_msec_int_t) (start->send_time - oldest) < 0)
{
oldest = start->send_time;
}
if (newest == NGX_TIMER_INFINITE || start->send_time > newest) {
if (nlost == 0
|| (ngx_msec_int_t) (start->send_time - newest) > 0)
{
newest = start->send_time;
}
@ -508,8 +674,9 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
* latest ACK frame.
*/
if (st && nlost >= 2 && (st->newest < oldest || st->oldest > newest)) {
if (st && nlost >= 2 && ((ngx_msec_int_t) (st->newest - oldest) < 0
|| (ngx_msec_int_t) (st->oldest - newest) > 0))
{
if (newest - oldest > ngx_quic_pcg_duration(c)) {
ngx_quic_persistent_congestion(c);
}
@ -547,11 +714,43 @@ ngx_quic_persistent_congestion(ngx_connection_t *c)
qc = ngx_quic_get_connection(c);
cg = &qc->congestion;
cg->recovery_start = ngx_current_msec;
cg->window = qc->tp.max_udp_payload_size * 2;
cg->mtu = qc->path->mtu;
cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
cg->window = cg->mtu * 2;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic persistent congestion win:%uz", cg->window);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion persistent t:%M win:%uz",
ngx_current_msec, cg->window);
}
static ngx_msec_t
ngx_quic_oldest_sent_packet(ngx_connection_t *c)
{
ngx_msec_t oldest;
ngx_uint_t i;
ngx_queue_t *q;
ngx_quic_frame_t *start;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
oldest = ngx_current_msec;
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
ctx = &qc->send_ctx[i];
if (!ngx_queue_empty(&ctx->sent)) {
q = ngx_queue_head(&ctx->sent);
start = ngx_queue_data(q, ngx_quic_frame_t, queue);
if ((ngx_msec_int_t) (start->send_time - oldest) < 0) {
oldest = start->send_time;
}
}
}
return oldest;
}
@ -588,7 +787,7 @@ ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
switch (f->type) {
case NGX_QUIC_FT_ACK:
case NGX_QUIC_FT_ACK_ECN:
if (ctx->level == ssl_encryption_application) {
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
/* force generation of most recent acknowledgment */
ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
}
@ -659,7 +858,7 @@ static void
ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
{
ngx_uint_t blocked;
ngx_msec_t timer;
ngx_msec_t now, timer;
ngx_quic_congestion_t *cg;
ngx_quic_connection_t *qc;
@ -681,26 +880,41 @@ ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
timer = f->send_time - cg->recovery_start;
now = ngx_current_msec;
if ((ngx_msec_int_t) timer <= 0) {
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion lost recovery win:%uz ss:%z if:%uz",
cg->window, cg->ssthresh, cg->in_flight);
"quic congestion lost rec t:%M win:%uz if:%uz",
now, cg->window, cg->in_flight);
goto done;
}
cg->recovery_start = ngx_current_msec;
cg->window /= 2;
if (f->ignore_loss) {
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion lost ignore t:%M win:%uz if:%uz",
now, cg->window, cg->in_flight);
if (cg->window < qc->tp.max_udp_payload_size * 2) {
cg->window = qc->tp.max_udp_payload_size * 2;
goto done;
}
cg->ssthresh = cg->window;
/* RFC 9438, 4.6. Multiplicative Decrease */
cg->mtu = qc->path->mtu;
cg->recovery_start = now;
cg->w_prior = cg->window;
/* RFC 9438, 4.7. Fast Convergence */
cg->w_max = (cg->window < cg->w_max)
? cg->window * (10 + NGX_QUIC_CUBIC_BETA) / 20 : cg->window;
cg->ssthresh = cg->in_flight * NGX_QUIC_CUBIC_BETA / 10;
cg->window = ngx_max(cg->ssthresh, cg->mtu * 2);
cg->w_est = cg->window;
cg->k = now + ngx_quic_congestion_cubic_time(c);
cg->idle_start = now;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion lost win:%uz ss:%z if:%uz",
cg->window, cg->ssthresh, cg->in_flight);
"quic congestion lost t:%M win:%uz if:%uz",
now, cg->window, cg->in_flight);
done:
@ -710,9 +924,62 @@ done:
}
static ngx_msec_t
ngx_quic_congestion_cubic_time(ngx_connection_t *c)
{
int64_t v, x, d, cc;
ngx_uint_t n;
ngx_quic_congestion_t *cg;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
cg = &qc->congestion;
/*
* RFC 9438, Figure 2
*
* k_msec = ((w_max - cwnd_epoch) / C / mtu) ^ 1/3 * 1000
*/
if (cg->w_max <= cg->window) {
return 0;
}
cc = 10000000000ll / (int64_t) cg->mtu / NGX_QUIC_CUBIC_C;
v = (int64_t) (cg->w_max - cg->window) * cc;
/*
* Newton-Raphson method for x ^ 3 = v:
*
* x_next = (2 * x_prev + v / x_prev ^ 2) / 3
*/
x = 5000;
for (n = 1; n <= 10; n++) {
d = (v / x / x - x) / 3;
x += d;
if (ngx_abs(d) <= 100) {
break;
}
}
if (x > NGX_MAX_SIZE_T_VALUE) {
return NGX_MAX_SIZE_T_VALUE;
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic cubic time:%L n:%ui", x, n);
return x;
}
void
ngx_quic_set_lost_timer(ngx_connection_t *c)
{
uint64_t pkt_thr;
ngx_uint_t i;
ngx_msec_t now;
ngx_queue_t *q;
@ -738,10 +1005,12 @@ ngx_quic_set_lost_timer(ngx_connection_t *c)
q = ngx_queue_head(&ctx->sent);
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
w = (ngx_msec_int_t)
(f->send_time + ngx_quic_lost_threshold(qc) - now);
(f->send_time + ngx_quic_time_threshold(qc) - now);
if (f->pnum <= ctx->largest_ack) {
if (w < 0 || ctx->largest_ack - f->pnum >= NGX_QUIC_PKT_THR) {
pkt_thr = ngx_quic_packet_threshold(ctx);
if (w < 0 || ctx->largest_ack - f->pnum >= pkt_thr) {
w = 0;
}
@ -804,7 +1073,7 @@ ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
duration = qc->avg_rtt;
duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);
if (ctx->level == ssl_encryption_application && c->ssl->handshaked) {
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION && c->ssl->handshaked) {
duration += qc->ctp.max_ack_delay;
}
@ -1159,7 +1428,7 @@ ngx_quic_generate_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
return NGX_OK;
}
if (ctx->level == ssl_encryption_application) {
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
delay = ngx_current_msec - ctx->ack_delay_start;
qc = ngx_quic_get_connection(c);

View File

@ -17,6 +17,7 @@ ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
void ngx_quic_congestion_ack(ngx_connection_t *c,
ngx_quic_frame_t *frame);
void ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle);
void ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
void ngx_quic_set_lost_timer(ngx_connection_t *c);
void ngx_quic_pto_handler(ngx_event_t *ev);

View File

@ -17,6 +17,15 @@
/* #define NGX_QUIC_DEBUG_ALLOC */ /* log frames and bufs alloc */
/* #define NGX_QUIC_DEBUG_CRYPTO */
#define NGX_QUIC_ENCRYPTION_INITIAL 0
#define NGX_QUIC_ENCRYPTION_EARLY_DATA 1
#define NGX_QUIC_ENCRYPTION_HANDSHAKE 2
#define NGX_QUIC_ENCRYPTION_APPLICATION 3
#define NGX_QUIC_ENCRYPTION_LAST 4
#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
typedef struct ngx_quic_connection_s ngx_quic_connection_t;
typedef struct ngx_quic_server_id_s ngx_quic_server_id_t;
typedef struct ngx_quic_client_id_s ngx_quic_client_id_t;
@ -46,8 +55,6 @@ typedef struct ngx_quic_keys_s ngx_quic_keys_t;
#define NGX_QUIC_UNSET_PN (uint64_t) -1
#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
/* 0-RTT and 1-RTT data exist in the same packet number space,
* so we have 3 packet number spaces:
*
@ -56,8 +63,8 @@ typedef struct ngx_quic_keys_s ngx_quic_keys_t;
* 2 - 0-RTT and 1-RTT
*/
#define ngx_quic_get_send_ctx(qc, level) \
((level) == ssl_encryption_initial) ? &((qc)->send_ctx[0]) \
: (((level) == ssl_encryption_handshake) ? &((qc)->send_ctx[1]) \
((level) == NGX_QUIC_ENCRYPTION_INITIAL) ? &((qc)->send_ctx[0]) \
: (((level) == NGX_QUIC_ENCRYPTION_HANDSHAKE) ? &((qc)->send_ctx[1]) \
: &((qc)->send_ctx[2]))
#define ngx_quic_get_connection(c) \
@ -168,7 +175,14 @@ typedef struct {
size_t in_flight;
size_t window;
size_t ssthresh;
size_t w_max;
size_t w_est;
size_t w_prior;
size_t mtu;
ngx_msec_t recovery_start;
ngx_msec_t idle_start;
ngx_msec_t k;
ngx_uint_t idle; /* unsigned idle:1; */
} ngx_quic_congestion_t;
@ -181,7 +195,7 @@ typedef struct {
* are also Initial packets.
*/
struct ngx_quic_send_ctx_s {
enum ssl_encryption_level_t level;
ngx_uint_t level;
ngx_quic_buffer_t crypto;
uint64_t crypto_sent;
@ -254,6 +268,7 @@ struct ngx_quic_connection_s {
ngx_buf_t *free_shadow_bufs;
ngx_uint_t nframes;
ngx_uint_t max_frames;
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_uint_t nbufs;
ngx_uint_t nshadowbufs;
@ -271,7 +286,7 @@ struct ngx_quic_connection_s {
off_t received;
ngx_uint_t error;
enum ssl_encryption_level_t error_level;
ngx_uint_t error_level;
ngx_uint_t error_ftype;
const char *error_reason;
@ -286,13 +301,17 @@ struct ngx_quic_connection_s {
unsigned key_phase:1;
unsigned validated:1;
unsigned client_tp_done:1;
#if (NGX_QUIC_OPENSSL_API)
unsigned read_level:2;
unsigned write_level:2;
#endif
};
ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c,
ngx_quic_tp_t *ctp);
void ngx_quic_discard_ctx(ngx_connection_t *c,
enum ssl_encryption_level_t level);
void ngx_quic_discard_ctx(ngx_connection_t *c, ngx_uint_t level);
void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
void ngx_quic_shutdown_quic(ngx_connection_t *c);

View File

@ -99,7 +99,7 @@ ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
frame->u.retire_cid.sequence_number = f->seqnum;
@ -452,7 +452,7 @@ ngx_quic_send_server_id(ngx_connection_t *c, ngx_quic_server_id_t *sid)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_NEW_CONNECTION_ID;
frame->u.ncid.seqnum = sid->seqnum;
frame->u.ncid.retire = 0;
@ -485,7 +485,7 @@ ngx_quic_free_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
frame->u.retire_cid.sequence_number = cid->seqnum;

View File

@ -214,7 +214,7 @@ ngx_quic_alloc_frame(ngx_connection_t *c)
"quic reuse frame n:%ui", qc->nframes);
#endif
} else if (qc->nframes < 10000) {
} else if (qc->nframes < qc->max_frames) {
frame = ngx_palloc(c->pool, sizeof(ngx_quic_frame_t));
if (frame == NULL) {
return NULL;

View File

@ -40,7 +40,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
ngx_quic_frame_t *fp;
ngx_quic_connection_t *qc;
if (pkt->level != ssl_encryption_application || pkt->path_challenged) {
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION || pkt->path_challenged) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ignoring PATH_CHALLENGE");
return NGX_OK;
@ -55,7 +55,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
return NGX_ERROR;
}
fp->level = ssl_encryption_application;
fp->level = NGX_QUIC_ENCRYPTION_APPLICATION;
fp->type = NGX_QUIC_FT_PATH_RESPONSE;
fp->u.path_response = *f;
@ -93,7 +93,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
return NGX_ERROR;
}
fp->level = ssl_encryption_application;
fp->level = NGX_QUIC_ENCRYPTION_APPLICATION;
fp->type = NGX_QUIC_FT_PING;
ngx_quic_queue_frame(qc, fp);
@ -177,16 +177,17 @@ valid:
if (rst) {
/* prevent old path packets contribution to congestion control */
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
qc->rst_pnum = ctx->pnum;
ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t));
qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
ngx_max(2 * qc->tp.max_udp_payload_size,
qc->congestion.window = ngx_min(10 * NGX_QUIC_MIN_INITIAL_SIZE,
ngx_max(2 * NGX_QUIC_MIN_INITIAL_SIZE,
14720));
qc->congestion.ssthresh = (size_t) -1;
qc->congestion.recovery_start = ngx_current_msec;
qc->congestion.mtu = NGX_QUIC_MIN_INITIAL_SIZE;
qc->congestion.recovery_start = ngx_current_msec - 1;
ngx_quic_init_rtt(qc);
}
@ -548,7 +549,7 @@ ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path)
(void) ngx_quic_send_path_challenge(c, path);
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
pto = ngx_max(ngx_quic_pto(c, ctx), 1000);
path->expires = ngx_current_msec + pto;
@ -578,7 +579,7 @@ ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_PATH_CHALLENGE;
ngx_memcpy(frame->u.path_challenge.data, path->challenge[n], 8);
@ -766,7 +767,7 @@ ngx_quic_expire_path_validation(ngx_connection_t *c, ngx_quic_path_t *path)
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
if (++path->tries < NGX_QUIC_PATH_RETRIES) {
pto = ngx_max(ngx_quic_pto(c, ctx), 1000) << path->tries;
@ -829,7 +830,7 @@ ngx_quic_expire_path_mtu_delay(ngx_connection_t *c, ngx_quic_path_t *path)
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
path->tries = 0;
@ -875,7 +876,7 @@ ngx_quic_expire_path_mtu_discovery(ngx_connection_t *c, ngx_quic_path_t *path)
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
if (++path->tries < NGX_QUIC_PATH_RETRIES) {
rc = ngx_quic_send_path_mtu_probe(c, path);
@ -921,11 +922,13 @@ ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_PING;
frame->ignore_loss = 1;
frame->ignore_congestion = 1;
qc = ngx_quic_get_connection(c);
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
pnum = ctx->pnum;
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,

View File

@ -35,8 +35,6 @@ typedef struct {
ngx_str_t payload;
uint64_t number;
ngx_quic_compat_keys_t *keys;
enum ssl_encryption_level_t level;
} ngx_quic_compat_record_t;
@ -435,11 +433,10 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
case SSL3_RT_HANDSHAKE:
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat tx %s len:%uz ",
ngx_quic_level_name(level), len);
"quic compat tx level:%d len:%uz", level, len);
if (com->method->add_handshake_data(ssl, level, buf, len) != 1) {
goto failed;
return;
}
break;
@ -449,11 +446,11 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
alert = ((u_char *) buf)[1];
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat %s alert:%ui len:%uz ",
ngx_quic_level_name(level), alert, len);
"quic compat level:%d alert:%ui len:%uz",
level, alert, len);
if (com->method->send_alert(ssl, level, alert) != 1) {
goto failed;
return;
}
}
@ -461,10 +458,6 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
}
return;
failed:
ngx_post_event(&qc->close, &ngx_posted_events);
}
@ -487,8 +480,8 @@ SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
c = ngx_ssl_get_connection(ssl);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat rx %s len:%uz",
ngx_quic_level_name(level), len);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat rx level:%d len:%uz", level, len);
qc = ngx_quic_get_connection(c);
com = qc->compat;
@ -501,7 +494,6 @@ SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
rec.log = c->log;
rec.number = com->read_record++;
rec.keys = &com->keys;
rec.level = level;
if (level == ssl_encryption_initial) {
n = ngx_min(len, 65535);

View File

@ -7,11 +7,6 @@
#ifndef _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
#define _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
#if defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION \
|| defined LIBRESSL_VERSION_NUMBER
#undef NGX_QUIC_OPENSSL_COMPAT
#else
#include <ngx_config.h>
#include <ngx_core.h>
@ -53,7 +48,4 @@ int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
void SSL_get_peer_quic_transport_params(const SSL *ssl,
const uint8_t **out_params, size_t *out_params_len);
#endif /* TLSEXT_TYPE_quic_transport_parameters */
#endif /* _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_ */

View File

@ -45,9 +45,9 @@
static ngx_int_t ngx_quic_create_datagrams(ngx_connection_t *c);
static void ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
static void ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
uint64_t pnum);
static void ngx_quic_commit_send(ngx_connection_t *c);
static void ngx_quic_revert_send(ngx_connection_t *c,
uint64_t preserved_pnum[NGX_QUIC_SEND_CTX_LAST]);
#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))
static ngx_uint_t ngx_quic_allow_segmentation(ngx_connection_t *c);
static ngx_int_t ngx_quic_create_segments(ngx_connection_t *c);
@ -55,7 +55,8 @@ static ssize_t ngx_quic_send_segments(ngx_connection_t *c, u_char *buf,
size_t len, struct sockaddr *sockaddr, socklen_t socklen, size_t segment);
#endif
static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min,
ngx_uint_t ack_only);
static void ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
ngx_quic_header_t *pkt, ngx_quic_path_t *path);
static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
@ -127,8 +128,11 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
cg = &qc->congestion;
path = qc->path;
while (cg->in_flight < cg->window) {
#if (NGX_SUPPRESS_WARN)
ngx_memzero(preserved_pnum, sizeof(preserved_pnum));
#endif
do {
p = dst;
len = ngx_quic_path_limit(c, path, path->mtu);
@ -150,16 +154,12 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
if (min > len) {
/* padding can't be applied - avoid sending the packet */
while (i-- > 0) {
ctx = &qc->send_ctx[i];
ngx_quic_revert_send(c, ctx, preserved_pnum[i]);
}
ngx_quic_revert_send(c, preserved_pnum);
return NGX_OK;
}
n = ngx_quic_output_packet(c, ctx, p, len, min);
n = ngx_quic_output_packet(c, ctx, p, len, min,
cg->in_flight >= cg->window);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
@ -180,37 +180,43 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
}
if (n == NGX_AGAIN) {
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
ngx_quic_revert_send(c, &qc->send_ctx[i], preserved_pnum[i]);
}
ngx_quic_revert_send(c, preserved_pnum);
ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
break;
}
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
ngx_quic_commit_send(c, &qc->send_ctx[i]);
}
ngx_quic_commit_send(c);
path->sent += len;
}
} while (cg->in_flight < cg->window);
return NGX_OK;
}
static void
ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
ngx_quic_commit_send(ngx_connection_t *c)
{
ngx_uint_t i, idle;
ngx_queue_t *q;
ngx_quic_frame_t *f;
ngx_quic_send_ctx_t *ctx;
ngx_quic_congestion_t *cg;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
cg = &qc->congestion;
idle = 1;
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
ctx = &qc->send_ctx[i];
if (!ngx_queue_empty(&ctx->frames)) {
idle = 0;
}
while (!ngx_queue_empty(&ctx->sending)) {
q = ngx_queue_head(&ctx->sending);
@ -227,26 +233,42 @@ ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
ngx_quic_free_frame(c, f);
}
}
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic congestion send if:%uz", cg->in_flight);
ngx_quic_congestion_idle(c, idle);
}
static void
ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
uint64_t pnum)
ngx_quic_revert_send(ngx_connection_t *c, uint64_t pnum[NGX_QUIC_SEND_CTX_LAST])
{
ngx_uint_t i;
ngx_queue_t *q;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
while (!ngx_queue_empty(&ctx->sending)) {
qc = ngx_quic_get_connection(c);
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
ctx = &qc->send_ctx[i];
if (ngx_queue_empty(&ctx->sending)) {
continue;
}
do {
q = ngx_queue_last(&ctx->sending);
ngx_queue_remove(q);
ngx_queue_insert_head(&ctx->frames, q);
} while (!ngx_queue_empty(&ctx->sending));
ctx->pnum = pnum[i];
}
ctx->pnum = pnum;
ngx_quic_congestion_idle(c, 1);
}
@ -272,17 +294,17 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
return 0;
}
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_INITIAL);
if (!ngx_queue_empty(&ctx->frames)) {
return 0;
}
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_HANDSHAKE);
if (!ngx_queue_empty(&ctx->frames)) {
return 0;
}
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
bytes = 0;
len = ngx_min(qc->path->mtu, NGX_QUIC_MAX_UDP_SEGMENT_BUF);
@ -295,6 +317,10 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
bytes += f->len;
if (qc->congestion.in_flight + bytes >= qc->congestion.window) {
return 0;
}
if (bytes > len * 3) {
/* require at least ~3 full packets to batch */
return 1;
@ -311,19 +337,19 @@ ngx_quic_create_segments(ngx_connection_t *c)
size_t len, segsize;
ssize_t n;
u_char *p, *end;
uint64_t preserved_pnum;
ngx_uint_t nseg;
ngx_uint_t nseg, level;
ngx_quic_path_t *path;
ngx_quic_send_ctx_t *ctx;
ngx_quic_congestion_t *cg;
ngx_quic_connection_t *qc;
static u_char dst[NGX_QUIC_MAX_UDP_SEGMENT_BUF];
static uint64_t preserved_pnum[NGX_QUIC_SEND_CTX_LAST];
qc = ngx_quic_get_connection(c);
cg = &qc->congestion;
path = qc->path;
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {
return NGX_ERROR;
@ -335,7 +361,8 @@ ngx_quic_create_segments(ngx_connection_t *c)
nseg = 0;
preserved_pnum = ctx->pnum;
level = ctx - qc->send_ctx;
preserved_pnum[level] = ctx->pnum;
for ( ;; ) {
@ -343,7 +370,7 @@ ngx_quic_create_segments(ngx_connection_t *c)
if (len && cg->in_flight + (p - dst) < cg->window) {
n = ngx_quic_output_packet(c, ctx, p, len, len);
n = ngx_quic_output_packet(c, ctx, p, len, len, 0);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
@ -369,19 +396,18 @@ ngx_quic_create_segments(ngx_connection_t *c)
}
if (n == NGX_AGAIN) {
ngx_quic_revert_send(c, ctx, preserved_pnum);
ngx_quic_revert_send(c, preserved_pnum);
ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
break;
}
ngx_quic_commit_send(c, ctx);
ngx_quic_commit_send(c);
path->sent += n;
p = dst;
nseg = 0;
preserved_pnum = ctx->pnum;
preserved_pnum[level] = ctx->pnum;
}
}
@ -411,7 +437,7 @@ ngx_quic_send_segments(ngx_connection_t *c, u_char *buf, size_t len,
ngx_memzero(msg_control, sizeof(msg_control));
iov.iov_len = len;
iov.iov_base = buf;
iov.iov_base = (void *) buf;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
@ -474,7 +500,7 @@ ngx_quic_get_padding_level(ngx_connection_t *c)
*/
qc = ngx_quic_get_connection(c);
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_INITIAL);
for (q = ngx_queue_head(&ctx->frames);
q != ngx_queue_sentinel(&ctx->frames);
@ -501,7 +527,7 @@ ngx_quic_get_padding_level(ngx_connection_t *c)
static ssize_t
ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
u_char *data, size_t max, size_t min)
u_char *data, size_t max, size_t min, ngx_uint_t ack_only)
{
size_t len, pad, min_payload, max_payload;
u_char *p;
@ -565,6 +591,10 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
{
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
if (ack_only && f->type != NGX_QUIC_FT_ACK) {
break;
}
if (len >= max_payload) {
break;
}
@ -657,10 +687,10 @@ ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
pkt->flags = NGX_QUIC_PKT_FIXED_BIT;
if (ctx->level == ssl_encryption_initial) {
if (ctx->level == NGX_QUIC_ENCRYPTION_INITIAL) {
pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
} else if (ctx->level == ssl_encryption_handshake) {
} else if (ctx->level == NGX_QUIC_ENCRYPTION_HANDSHAKE) {
pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;
} else {
@ -699,7 +729,7 @@ ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len,
ngx_memzero(&msg, sizeof(struct msghdr));
iov.iov_len = len;
iov.iov_base = buf;
iov.iov_base = (void *) buf;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
@ -1073,7 +1103,7 @@ ngx_quic_send_new_token(ngx_connection_t *c, ngx_quic_path_t *path)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_NEW_TOKEN;
frame->data = out;
frame->u.token.length = token.len;

View File

@ -8,6 +8,12 @@
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_quic_connection.h>
#if (NGX_QUIC_BORINGSSL_EVP_API)
#include <openssl/hkdf.h>
#include <openssl/chacha.h>
#else
#include <openssl/kdf.h>
#endif
/* RFC 9001, 5.4.1. Header Protection Application: 5-byte mask */
@ -33,7 +39,7 @@ static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,
static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out,
const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
#ifndef OPENSSL_IS_BORINGSSL
#if !(NGX_QUIC_BORINGSSL_EVP_API)
static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
#endif
@ -58,7 +64,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
switch (id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
ciphers->c = EVP_aead_aes_128_gcm();
#else
ciphers->c = EVP_aes_128_gcm();
@ -69,7 +75,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
break;
case TLS1_3_CK_AES_256_GCM_SHA384:
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
ciphers->c = EVP_aead_aes_256_gcm();
#else
ciphers->c = EVP_aes_256_gcm();
@ -80,12 +86,12 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
break;
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
ciphers->c = EVP_aead_chacha20_poly1305();
#else
ciphers->c = EVP_chacha20_poly1305();
#endif
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305();
#else
ciphers->hp = EVP_chacha20();
@ -94,7 +100,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
len = 32;
break;
#ifndef OPENSSL_IS_BORINGSSL
#if !(NGX_QUIC_BORINGSSL_EVP_API)
case TLS1_3_CK_AES_128_CCM_SHA256:
ciphers->c = EVP_aes_128_ccm();
ciphers->hp = EVP_aes_128_ctr();
@ -125,12 +131,13 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret,
ngx_quic_secret_t *client, *server;
ngx_quic_ciphers_t ciphers;
static const uint8_t salt[20] =
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17"
"\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a";
static const uint8_t salt[20] = {
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
};
client = &keys->secrets[ssl_encryption_initial].client;
server = &keys->secrets[ssl_encryption_initial].server;
client = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].client;
server = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].server;
/*
* RFC 9001, section 5. Packet Protection
@ -262,7 +269,7 @@ static ngx_int_t
ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest,
const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len)
{
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len)
== 0)
@ -324,7 +331,7 @@ ngx_hkdf_extract(u_char *out_key, size_t *out_len, const EVP_MD *digest,
const u_char *secret, size_t secret_len, const u_char *salt,
size_t salt_len)
{
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt,
salt_len)
@ -387,7 +394,7 @@ ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s,
ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log)
{
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
EVP_AEAD_CTX *ctx;
ctx = EVP_AEAD_CTX_new(cipher, key->data, key->len,
@ -447,7 +454,7 @@ static ngx_int_t
ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
{
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
if (EVP_AEAD_CTX_open(s->ctx, out->data, &out->len, out->len, nonce,
s->iv.len, in->data, in->len, ad->data, ad->len)
!= 1)
@ -467,7 +474,7 @@ ngx_int_t
ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
{
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
if (EVP_AEAD_CTX_seal(s->ctx, out->data, &out->len, out->len, nonce,
s->iv.len, in->data, in->len, ad->data, ad->len)
!= 1)
@ -483,7 +490,7 @@ ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
}
#ifndef OPENSSL_IS_BORINGSSL
#if !(NGX_QUIC_BORINGSSL_EVP_API)
static ngx_int_t
ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
@ -562,7 +569,7 @@ void
ngx_quic_crypto_cleanup(ngx_quic_secret_t *s)
{
if (s->ctx) {
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
EVP_AEAD_CTX_free(s->ctx);
#else
EVP_CIPHER_CTX_free(s->ctx);
@ -578,7 +585,7 @@ ngx_quic_crypto_hp_init(const EVP_CIPHER *cipher, ngx_quic_secret_t *s,
{
EVP_CIPHER_CTX *ctx;
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
if (cipher == (EVP_CIPHER *) EVP_aead_chacha20_poly1305()) {
/* no EVP interface */
s->hp_ctx = NULL;
@ -614,7 +621,7 @@ ngx_quic_crypto_hp(ngx_quic_secret_t *s, u_char *out, u_char *in,
ctx = s->hp_ctx;
#ifdef OPENSSL_IS_BORINGSSL
#if (NGX_QUIC_BORINGSSL_EVP_API)
uint32_t cnt;
if (ctx == NULL) {
@ -655,8 +662,8 @@ ngx_quic_crypto_hp_cleanup(ngx_quic_secret_t *s)
ngx_int_t
ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write,
ngx_quic_keys_t *keys, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
ngx_quic_keys_t *keys, ngx_uint_t level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len)
{
ngx_int_t key_len;
ngx_str_t secret_str;
@ -721,8 +728,8 @@ ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write,
ngx_uint_t
ngx_quic_keys_available(ngx_quic_keys_t *keys,
enum ssl_encryption_level_t level, ngx_uint_t is_write)
ngx_quic_keys_available(ngx_quic_keys_t *keys, ngx_uint_t level,
ngx_uint_t is_write)
{
if (is_write == 0) {
return keys->secrets[level].client.ctx != NULL;
@ -733,8 +740,7 @@ ngx_quic_keys_available(ngx_quic_keys_t *keys,
void
ngx_quic_keys_discard(ngx_quic_keys_t *keys,
enum ssl_encryption_level_t level)
ngx_quic_keys_discard(ngx_quic_keys_t *keys, ngx_uint_t level)
{
ngx_quic_secret_t *client, *server;
@ -764,7 +770,7 @@ ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys)
{
ngx_quic_secrets_t *current, *next, tmp;
current = &keys->secrets[ssl_encryption_application];
current = &keys->secrets[NGX_QUIC_ENCRYPTION_APPLICATION];
next = &keys->next_key;
ngx_quic_crypto_cleanup(&current->client);
@ -793,7 +799,7 @@ ngx_quic_keys_update(ngx_event_t *ev)
qc = ngx_quic_get_connection(c);
keys = qc->keys;
current = &keys->secrets[ssl_encryption_application];
current = &keys->secrets[NGX_QUIC_ENCRYPTION_APPLICATION];
next = &keys->next_key;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic key update");
@ -958,8 +964,9 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res)
/* 5.8. Retry Packet Integrity */
static ngx_quic_md_t key = ngx_quic_md(
"\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e");
static const u_char nonce[NGX_QUIC_IV_LEN] =
"\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb";
static const u_char nonce[NGX_QUIC_IV_LEN] = {
0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb
};
static ngx_str_t in = ngx_string("");
ad.data = res->data;

View File

@ -14,8 +14,6 @@
#include <ngx_event_quic_transport.h>
#define NGX_QUIC_ENCRYPTION_LAST ((ssl_encryption_application) + 1)
/* RFC 5116, 5.1/5.3 and RFC 8439, 2.3/2.5 for all supported ciphers */
#define NGX_QUIC_IV_LEN 12
#define NGX_QUIC_TAG_LEN 16
@ -24,10 +22,12 @@
#define NGX_QUIC_MAX_MD_SIZE 48
#ifdef OPENSSL_IS_BORINGSSL
#if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC)
#define NGX_QUIC_BORINGSSL_EVP_API 1
#define ngx_quic_cipher_t EVP_AEAD
#define ngx_quic_crypto_ctx_t EVP_AEAD_CTX
#else
#define NGX_QUIC_BORINGSSL_EVP_API 0
#define ngx_quic_cipher_t EVP_CIPHER
#define ngx_quic_crypto_ctx_t EVP_CIPHER_CTX
#endif
@ -94,13 +94,11 @@ typedef struct {
ngx_int_t ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys,
ngx_str_t *secret, ngx_log_t *log);
ngx_int_t ngx_quic_keys_set_encryption_secret(ngx_log_t *log,
ngx_uint_t is_write, ngx_quic_keys_t *keys,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len);
ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys,
enum ssl_encryption_level_t level, ngx_uint_t is_write);
void ngx_quic_keys_discard(ngx_quic_keys_t *keys,
enum ssl_encryption_level_t level);
ngx_uint_t is_write, ngx_quic_keys_t *keys, ngx_uint_t level,
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);
ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys, ngx_uint_t level,
ngx_uint_t is_write);
void ngx_quic_keys_discard(ngx_quic_keys_t *keys, ngx_uint_t level);
void ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys);
void ngx_quic_keys_update(ngx_event_t *ev);
void ngx_quic_keys_cleanup(ngx_quic_keys_t *keys);

View File

@ -10,13 +10,6 @@
#include <ngx_event_quic_connection.h>
#if defined OPENSSL_IS_BORINGSSL \
|| defined LIBRESSL_VERSION_NUMBER \
|| NGX_QUIC_OPENSSL_COMPAT
#define NGX_QUIC_BORINGSSL_API 1
#endif
/*
* RFC 9000, 7.5. Cryptographic Message Buffering
*
@ -25,43 +18,343 @@
#define NGX_QUIC_MAX_BUFFERED 65535
#if (NGX_QUIC_OPENSSL_API)
static int ngx_quic_cbs_send(ngx_ssl_conn_t *ssl_conn,
const unsigned char *data, size_t len, size_t *consumed, void *arg);
static int ngx_quic_cbs_recv_rcd(ngx_ssl_conn_t *ssl_conn,
const unsigned char **data, size_t *bytes_read, void *arg);
static int ngx_quic_cbs_release_rcd(ngx_ssl_conn_t *ssl_conn,
size_t bytes_read, void *arg);
static int ngx_quic_cbs_yield_secret(ngx_ssl_conn_t *ssl_conn, uint32_t level,
int direction, const unsigned char *secret, size_t secret_len, void *arg);
static int ngx_quic_cbs_got_transport_params(ngx_ssl_conn_t *ssl_conn,
const unsigned char *params, size_t params_len, void *arg);
static int ngx_quic_cbs_alert(ngx_ssl_conn_t *ssl_conn, unsigned char alert,
void *arg);
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
static ngx_inline ngx_uint_t ngx_quic_map_encryption_level(
enum ssl_encryption_level_t ssl_level);
#if (NGX_QUIC_BORINGSSL_API)
static int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len);
static int ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len);
#else
#else /* NGX_QUIC_QUICTLS_API */
static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *read_secret,
enum ssl_encryption_level_t ssl_level, const uint8_t *read_secret,
const uint8_t *write_secret, size_t secret_len);
#endif
static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *data, size_t len);
enum ssl_encryption_level_t ssl_level, const uint8_t *data, size_t len);
static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, uint8_t alert);
static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
enum ssl_encryption_level_t level);
enum ssl_encryption_level_t ssl_level, uint8_t alert);
#endif
static ngx_int_t ngx_quic_handshake(ngx_connection_t *c);
static ngx_int_t ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level);
#if (NGX_QUIC_OPENSSL_API)
static int
ngx_quic_cbs_send(ngx_ssl_conn_t *ssl_conn,
const unsigned char *data, size_t len, size_t *consumed, void *arg)
{
ngx_connection_t *c = arg;
ngx_chain_t *out;
unsigned int alpn_len;
ngx_quic_frame_t *frame;
const unsigned char *alpn_data;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_cbs_send len:%uz", len);
qc = ngx_quic_get_connection(c);
*consumed = 0;
SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);
if (alpn_len == 0) {
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
qc->error_reason = "missing ALPN extension";
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic missing ALPN extension");
return 1;
}
if (!qc->client_tp_done) {
/* RFC 9001, 8.2. QUIC Transport Parameters Extension */
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
qc->error_reason = "missing transport parameters";
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"missing transport parameters");
return 1;
}
ctx = ngx_quic_get_send_ctx(qc, qc->write_level);
out = ngx_quic_copy_buffer(c, (u_char *) data, len);
if (out == NGX_CHAIN_ERROR) {
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
return 1;
}
frame = ngx_quic_alloc_frame(c);
if (frame == NULL) {
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
return 1;
}
frame->data = out;
frame->level = qc->write_level;
frame->type = NGX_QUIC_FT_CRYPTO;
frame->u.crypto.offset = ctx->crypto_sent;
frame->u.crypto.length = len;
ctx->crypto_sent += len;
*consumed = len;
ngx_quic_queue_frame(qc, frame);
return 1;
}
static int
ngx_quic_cbs_recv_rcd(ngx_ssl_conn_t *ssl_conn,
const unsigned char **data, size_t *bytes_read, void *arg)
{
ngx_connection_t *c = arg;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_cbs_recv_rcd");
qc = ngx_quic_get_connection(c);
ctx = ngx_quic_get_send_ctx(qc, qc->read_level);
for (cl = ctx->crypto.chain; cl; cl = cl->next) {
b = cl->buf;
if (b->sync) {
/* hole */
*bytes_read = 0;
break;
}
*data = b->pos;
*bytes_read = b->last - b->pos;
break;
}
return 1;
}
static int
ngx_quic_cbs_release_rcd(ngx_ssl_conn_t *ssl_conn, size_t bytes_read, void *arg)
{
ngx_connection_t *c = arg;
ngx_chain_t *cl;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_cbs_release_rcd len:%uz", bytes_read);
qc = ngx_quic_get_connection(c);
ctx = ngx_quic_get_send_ctx(qc, qc->read_level);
cl = ngx_quic_read_buffer(c, &ctx->crypto, bytes_read);
if (cl == NGX_CHAIN_ERROR) {
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
return 1;
}
ngx_quic_free_chain(c, cl);
return 1;
}
static int
ngx_quic_cbs_yield_secret(ngx_ssl_conn_t *ssl_conn, uint32_t ssl_level,
int direction, const unsigned char *secret, size_t secret_len, void *arg)
{
ngx_connection_t *c = arg;
ngx_uint_t level;
const SSL_CIPHER *cipher;
ngx_quic_connection_t *qc;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_cbs_yield_secret() level:%uD", ssl_level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic %s secret len:%uz %*xs",
direction ? "write" : "read", secret_len,
secret_len, secret);
#endif
qc = ngx_quic_get_connection(c);
cipher = SSL_get_current_cipher(ssl_conn);
switch (ssl_level) {
case OSSL_RECORD_PROTECTION_LEVEL_NONE:
level = NGX_QUIC_ENCRYPTION_INITIAL;
break;
case OSSL_RECORD_PROTECTION_LEVEL_EARLY:
level = NGX_QUIC_ENCRYPTION_EARLY_DATA;
break;
case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE:
level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
break;
default: /* OSSL_RECORD_PROTECTION_LEVEL_APPLICATION */
level = NGX_QUIC_ENCRYPTION_APPLICATION;
break;
}
if (ngx_quic_keys_set_encryption_secret(c->log, direction, qc->keys, level,
cipher, secret, secret_len)
!= NGX_OK)
{
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
return 1;
}
if (direction) {
qc->write_level = level;
} else {
qc->read_level = level;
}
return 1;
}
static int
ngx_quic_cbs_got_transport_params(ngx_ssl_conn_t *ssl_conn,
const unsigned char *params, size_t params_len, void *arg)
{
ngx_connection_t *c = arg;
u_char *p, *end;
ngx_quic_tp_t ctp;
ngx_quic_connection_t *qc;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_cbs_got_transport_params() len:%uz",
params_len);
qc = ngx_quic_get_connection(c);
/* defaults for parameters not sent by client */
ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t));
p = (u_char *) params;
end = p + params_len;
if (ngx_quic_parse_transport_params(p, end, &ctp, c->log) != NGX_OK) {
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
qc->error_reason = "failed to process transport parameters";
return 1;
}
if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
return 1;
}
qc->client_tp_done = 1;
return 1;
}
static int
ngx_quic_cbs_alert(ngx_ssl_conn_t *ssl_conn, unsigned char alert, void *arg)
{
ngx_connection_t *c = arg;
ngx_quic_connection_t *qc;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_cbs_alert() alert:%d", (int) alert);
/* already closed on regular shutdown */
qc = ngx_quic_get_connection(c);
if (qc == NULL) {
return 1;
}
qc->error = NGX_QUIC_ERR_CRYPTO(alert);
qc->error_reason = "handshake failed";
return 1;
}
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
static ngx_inline ngx_uint_t
ngx_quic_map_encryption_level(enum ssl_encryption_level_t ssl_level)
{
switch (ssl_level) {
case ssl_encryption_initial:
return NGX_QUIC_ENCRYPTION_INITIAL;
case ssl_encryption_early_data:
return NGX_QUIC_ENCRYPTION_EARLY_DATA;
case ssl_encryption_handshake:
return NGX_QUIC_ENCRYPTION_HANDSHAKE;
default: /* ssl_encryption_application */
return NGX_QUIC_ENCRYPTION_APPLICATION;
}
}
#if (NGX_QUIC_BORINGSSL_API)
static int
ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
const uint8_t *rsecret, size_t secret_len)
{
ngx_uint_t level;
ngx_connection_t *c;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
c = ngx_ssl_get_connection(ssl_conn);
qc = ngx_quic_get_connection(c);
level = ngx_quic_map_encryption_level(ssl_level);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_set_read_secret() level:%d", level);
"quic ngx_quic_set_read_secret() level:%d", ssl_level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic read secret len:%uz %*xs", secret_len,
@ -72,7 +365,7 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
cipher, rsecret, secret_len)
!= NGX_OK)
{
return 0;
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
}
return 1;
@ -81,17 +374,19 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
static int
ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
const uint8_t *wsecret, size_t secret_len)
{
ngx_uint_t level;
ngx_connection_t *c;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
c = ngx_ssl_get_connection(ssl_conn);
qc = ngx_quic_get_connection(c);
level = ngx_quic_map_encryption_level(ssl_level);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_set_write_secret() level:%d", level);
"quic ngx_quic_set_write_secret() level:%d", ssl_level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic write secret len:%uz %*xs", secret_len,
@ -102,28 +397,31 @@ ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
cipher, wsecret, secret_len)
!= NGX_OK)
{
return 0;
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
}
return 1;
}
#else
#else /* NGX_QUIC_QUICTLS_API */
static int
ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *rsecret,
enum ssl_encryption_level_t ssl_level, const uint8_t *rsecret,
const uint8_t *wsecret, size_t secret_len)
{
ngx_uint_t level;
ngx_connection_t *c;
const SSL_CIPHER *cipher;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
c = ngx_ssl_get_connection(ssl_conn);
qc = ngx_quic_get_connection(c);
level = ngx_quic_map_encryption_level(ssl_level);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_set_encryption_secrets() level:%d", level);
"quic ngx_quic_set_encryption_secrets() level:%d",
ssl_level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic read secret len:%uz %*xs", secret_len,
@ -136,10 +434,11 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
cipher, rsecret, secret_len)
!= NGX_OK)
{
return 0;
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
return 1;
}
if (level == ssl_encryption_early_data) {
if (level == NGX_QUIC_ENCRYPTION_EARLY_DATA) {
return 1;
}
@ -153,7 +452,7 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
cipher, wsecret, secret_len)
!= NGX_OK)
{
return 0;
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
}
return 1;
@ -164,24 +463,24 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
static int
ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *data, size_t len)
enum ssl_encryption_level_t ssl_level, const uint8_t *data, size_t len)
{
u_char *p, *end;
size_t client_params_len;
ngx_uint_t level;
ngx_chain_t *out;
unsigned int alpn_len;
const uint8_t *client_params;
ngx_quic_tp_t ctp;
ngx_quic_frame_t *frame;
ngx_connection_t *c;
const unsigned char *alpn_data;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
unsigned int alpn_len;
const unsigned char *alpn_data;
#endif
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
c = ngx_ssl_get_connection(ssl_conn);
qc = ngx_quic_get_connection(c);
level = ngx_quic_map_encryption_level(ssl_level);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_add_handshake_data");
@ -193,20 +492,19 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
* here;
*/
#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);
if (alpn_len == 0) {
if (qc->error == 0) {
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
qc->error_reason = "unsupported protocol in ALPN extension";
qc->error_reason = "missing ALPN extension";
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic unsupported protocol in ALPN extension");
return 0;
"quic missing ALPN extension");
}
#endif
return 1;
}
SSL_get_peer_quic_transport_params(ssl_conn, &client_params,
&client_params_len);
@ -217,12 +515,16 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
if (client_params_len == 0) {
/* RFC 9001, 8.2. QUIC Transport Parameters Extension */
if (qc->error == 0) {
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
qc->error_reason = "missing transport parameters";
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"missing transport parameters");
return 0;
}
return 1;
}
p = (u_char *) client_params;
@ -237,11 +539,11 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
qc->error_reason = "failed to process transport parameters";
return 0;
return 1;
}
if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
return 0;
return 1;
}
qc->client_tp_done = 1;
@ -251,12 +553,14 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
out = ngx_quic_copy_buffer(c, (u_char *) data, len);
if (out == NGX_CHAIN_ERROR) {
return 0;
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
return 1;
}
frame = ngx_quic_alloc_frame(c);
if (frame == NULL) {
return 0;
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
return 1;
}
frame->data = out;
@ -279,7 +583,7 @@ ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
#if (NGX_DEBUG)
ngx_connection_t *c;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
c = ngx_ssl_get_connection(ssl_conn);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_flush_flight()");
@ -289,17 +593,17 @@ ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
static int
ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
uint8_t alert)
ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t ssl_level, uint8_t alert)
{
ngx_connection_t *c;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
c = ngx_ssl_get_connection(ssl_conn);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_send_alert() level:%s alert:%d",
ngx_quic_level_name(level), (int) alert);
"quic ngx_quic_send_alert() level:%d alert:%d",
ssl_level, (int) alert);
/* already closed on regular shutdown */
@ -314,13 +618,14 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
return 1;
}
#endif
ngx_int_t
ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
ngx_quic_frame_t *frame)
{
uint64_t last;
ngx_chain_t *cl;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
ngx_quic_crypto_frame_t *f;
@ -343,13 +648,13 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
}
if (last <= ctx->crypto.offset) {
if (pkt->level == ssl_encryption_initial) {
if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
/* speeding up handshake completion */
if (!ngx_queue_empty(&ctx->sent)) {
ngx_quic_resend_frames(c, ctx);
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_HANDSHAKE);
while (!ngx_queue_empty(&ctx->sent)) {
ngx_quic_resend_frames(c, ctx);
}
@ -359,43 +664,25 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
return NGX_OK;
}
if (f->offset == ctx->crypto.offset) {
if (ngx_quic_crypto_input(c, frame->data, pkt->level) != NGX_OK) {
return NGX_ERROR;
}
ngx_quic_skip_buffer(c, &ctx->crypto, last);
} else {
if (ngx_quic_write_buffer(c, &ctx->crypto, frame->data, f->length,
f->offset)
== NGX_CHAIN_ERROR)
{
return NGX_ERROR;
}
}
cl = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1);
if (cl) {
if (ngx_quic_crypto_input(c, cl, pkt->level) != NGX_OK) {
if (ngx_quic_crypto_provide(c, pkt->level) != NGX_OK) {
return NGX_ERROR;
}
ngx_quic_free_chain(c, cl);
}
return NGX_OK;
return ngx_quic_handshake(c);
}
static ngx_int_t
ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
enum ssl_encryption_level_t level)
ngx_quic_handshake(ngx_connection_t *c)
{
int n, sslerr;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_ssl_conn_t *ssl_conn;
ngx_quic_frame_t *frame;
ngx_quic_connection_t *qc;
@ -404,16 +691,6 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
ssl_conn = c->ssl->connection;
for (cl = data; cl; cl = cl->next) {
b = cl->buf;
if (!SSL_provide_quic_data(ssl_conn, level, b->pos, b->last - b->pos)) {
ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
"SSL_provide_quic_data() failed");
return NGX_ERROR;
}
}
n = SSL_do_handshake(ssl_conn);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
@ -424,22 +701,31 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d",
sslerr);
if (sslerr != SSL_ERROR_WANT_READ) {
if (c->ssl->handshake_rejected) {
ngx_connection_error(c, 0, "handshake rejected");
ERR_clear_error();
return NGX_ERROR;
}
ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed");
if (qc->error) {
ngx_connection_error(c, 0, "SSL_do_handshake() failed");
ERR_clear_error();
return NGX_ERROR;
}
if (sslerr != SSL_ERROR_WANT_READ) {
ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed");
return NGX_ERROR;
}
}
if (n <= 0 || SSL_in_init(ssl_conn)) {
if (ngx_quic_keys_available(qc->keys, ssl_encryption_early_data, 0)
if (qc->error) {
ngx_connection_error(c, 0, "SSL_do_handshake() failed");
return NGX_ERROR;
}
if (!SSL_is_init_finished(ssl_conn)) {
if (ngx_quic_keys_available(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA, 0)
&& qc->client_tp_done)
{
if (ngx_quic_init_streams(c) != NGX_OK) {
@ -461,7 +747,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_HANDSHAKE_DONE;
ngx_quic_queue_frame(qc, frame);
@ -485,7 +771,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
* An endpoint MUST discard its Handshake keys
* when the TLS handshake is confirmed.
*/
ngx_quic_discard_ctx(c, ssl_encryption_handshake);
ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_HANDSHAKE);
ngx_quic_discover_path_mtu(c, qc->path);
@ -502,6 +788,60 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
}
static ngx_int_t
ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level)
{
#if (NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API)
ngx_buf_t *b;
ngx_chain_t *out, *cl;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
enum ssl_encryption_level_t ssl_level;
qc = ngx_quic_get_connection(c);
ctx = ngx_quic_get_send_ctx(qc, level);
out = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1);
if (out == NGX_CHAIN_ERROR) {
return NGX_ERROR;
}
switch (level) {
case NGX_QUIC_ENCRYPTION_INITIAL:
ssl_level = ssl_encryption_initial;
break;
case NGX_QUIC_ENCRYPTION_EARLY_DATA:
ssl_level = ssl_encryption_early_data;
break;
case NGX_QUIC_ENCRYPTION_HANDSHAKE:
ssl_level = ssl_encryption_handshake;
break;
default: /* NGX_QUIC_ENCRYPTION_APPLICATION */
ssl_level = ssl_encryption_application;
break;
}
for (cl = out; cl; cl = cl->next) {
b = cl->buf;
if (!SSL_provide_quic_data(c->ssl->connection, ssl_level, b->pos,
b->last - b->pos))
{
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"SSL_provide_quic_data() failed");
return NGX_ERROR;
}
}
ngx_quic_free_chain(c, out);
#endif
return NGX_OK;
}
ngx_int_t
ngx_quic_init_connection(ngx_connection_t *c)
{
@ -512,7 +852,33 @@ ngx_quic_init_connection(ngx_connection_t *c)
ngx_ssl_conn_t *ssl_conn;
ngx_quic_socket_t *qsock;
ngx_quic_connection_t *qc;
#if (NGX_QUIC_OPENSSL_API)
static const OSSL_DISPATCH qtdis[] = {
{ OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND,
(void (*)(void)) ngx_quic_cbs_send },
{ OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD,
(void (*)(void)) ngx_quic_cbs_recv_rcd },
{ OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD,
(void (*)(void)) ngx_quic_cbs_release_rcd },
{ OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET,
(void (*)(void)) ngx_quic_cbs_yield_secret },
{ OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS,
(void (*)(void)) ngx_quic_cbs_got_transport_params },
{ OSSL_FUNC_SSL_QUIC_TLS_ALERT,
(void (*)(void)) ngx_quic_cbs_alert },
{ 0, NULL }
};
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
static SSL_QUIC_METHOD quic_method;
#endif
qc = ngx_quic_get_connection(c);
@ -524,6 +890,20 @@ ngx_quic_init_connection(ngx_connection_t *c)
ssl_conn = c->ssl->connection;
#if (NGX_QUIC_OPENSSL_API)
if (SSL_set_quic_tls_cbs(ssl_conn, qtdis, c) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"quic SSL_set_quic_tls_cbs() failed");
return NGX_ERROR;
}
if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
SSL_set_quic_tls_early_data_enabled(ssl_conn, 1);
}
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
if (!quic_method.send_alert) {
#if (NGX_QUIC_BORINGSSL_API)
quic_method.set_read_secret = ngx_quic_set_read_secret;
@ -537,15 +917,17 @@ ngx_quic_init_connection(ngx_connection_t *c)
}
if (SSL_set_quic_method(ssl_conn, &quic_method) == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"quic SSL_set_quic_method() failed");
return NGX_ERROR;
}
#ifdef OPENSSL_INFO_QUIC
#if (NGX_QUIC_QUICTLS_API)
if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
SSL_set_quic_early_data_enabled(ssl_conn, 1);
}
#endif
#endif
qsock = ngx_quic_get_socket(c);
@ -577,15 +959,23 @@ ngx_quic_init_connection(ngx_connection_t *c)
"quic transport parameters len:%uz %*xs", len, len, p);
#endif
#if (NGX_QUIC_OPENSSL_API)
if (SSL_set_quic_tls_transport_params(ssl_conn, p, len) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"quic SSL_set_quic_tls_transport_params() failed");
return NGX_ERROR;
}
#else
if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"quic SSL_set_quic_transport_params() failed");
return NGX_ERROR;
}
#endif
#ifdef OPENSSL_IS_BORINGSSL
#if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC)
if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"quic SSL_set_quic_early_data_context() failed");
return NGX_ERROR;
}

View File

@ -174,7 +174,7 @@ ngx_int_t
ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc)
{
ngx_pool_t *pool;
ngx_queue_t *q;
ngx_queue_t *q, posted_events;
ngx_rbtree_t *tree;
ngx_connection_t *sc;
ngx_rbtree_node_t *node;
@ -197,6 +197,8 @@ ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc)
return NGX_OK;
}
ngx_queue_init(&posted_events);
node = ngx_rbtree_min(tree->root, tree->sentinel);
while (node) {
@ -213,15 +215,21 @@ ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc)
}
sc->read->error = 1;
sc->read->ready = 1;
sc->write->error = 1;
ngx_quic_set_event(sc->read);
ngx_quic_set_event(sc->write);
sc->write->ready = 1;
sc->close = 1;
sc->read->handler(sc->read);
if (sc->read->posted) {
ngx_delete_posted_event(sc->read);
}
ngx_post_event(sc->read, &posted_events);
}
ngx_event_process_posted((ngx_cycle_t *) ngx_cycle, &posted_events);
if (tree->root == tree->sentinel) {
return NGX_OK;
}
@ -272,7 +280,7 @@ ngx_quic_do_reset_stream(ngx_quic_stream_t *qs, ngx_uint_t err)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_RESET_STREAM;
frame->u.reset_stream.id = qs->id;
frame->u.reset_stream.error_code = err;
@ -359,7 +367,7 @@ ngx_quic_shutdown_stream_recv(ngx_connection_t *c)
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, 0,
"quic stream id:0x%xL recv shutdown", qs->id);
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_STOP_SENDING;
frame->u.stop_sending.id = qs->id;
frame->u.stop_sending.error_code = qc->conf->stream_close_code;
@ -519,7 +527,7 @@ ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_RESET_STREAM;
frame->u.reset_stream.id = id;
frame->u.reset_stream.error_code = code;
@ -532,7 +540,7 @@ ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_STOP_SENDING;
frame->u.stop_sending.id = id;
frame->u.stop_sending.error_code = code;
@ -1054,7 +1062,7 @@ ngx_quic_stream_flush(ngx_quic_stream_t *qs)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_STREAM;
frame->data = out;
@ -1172,7 +1180,7 @@ ngx_quic_close_stream(ngx_quic_stream_t *qs)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_MAX_STREAMS;
if (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {
@ -1763,7 +1771,7 @@ ngx_quic_update_max_stream_data(ngx_quic_stream_t *qs)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_MAX_STREAM_DATA;
frame->u.max_stream_data.id = qs->id;
frame->u.max_stream_data.limit = qs->recv_max_data;
@ -1799,7 +1807,7 @@ ngx_quic_update_max_data(ngx_connection_t *c)
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_MAX_DATA;
frame->u.max_data.max_data = qc->streams.recv_max_data;

View File

@ -281,7 +281,7 @@ ngx_int_t
ngx_quic_parse_packet(ngx_quic_header_t *pkt)
{
if (!ngx_quic_long_pkt(pkt->flags)) {
pkt->level = ssl_encryption_application;
pkt->level = NGX_QUIC_ENCRYPTION_APPLICATION;
if (ngx_quic_parse_short_header(pkt, NGX_QUIC_SERVER_CID_LEN) != NGX_OK)
{
@ -295,6 +295,11 @@ ngx_quic_parse_packet(ngx_quic_header_t *pkt)
return NGX_ERROR;
}
if (pkt->version == 0) {
/* version negotiation */
return NGX_ERROR;
}
if (!ngx_quic_supported_version(pkt->version)) {
return NGX_ABORT;
}
@ -463,13 +468,13 @@ ngx_quic_parse_long_header_v1(ngx_quic_header_t *pkt)
return NGX_ERROR;
}
pkt->level = ssl_encryption_initial;
pkt->level = NGX_QUIC_ENCRYPTION_INITIAL;
} else if (ngx_quic_pkt_zrtt(pkt->flags)) {
pkt->level = ssl_encryption_early_data;
pkt->level = NGX_QUIC_ENCRYPTION_EARLY_DATA;
} else if (ngx_quic_pkt_hs(pkt->flags)) {
pkt->level = ssl_encryption_handshake;
pkt->level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
} else {
ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
@ -588,7 +593,7 @@ ngx_quic_payload_size(ngx_quic_header_t *pkt, size_t pkt_len)
/* flags, version, dcid and scid with lengths and zero-length token */
len = 5 + 2 + pkt->dcid.len + pkt->scid.len
+ (pkt->level == ssl_encryption_initial ? 1 : 0);
+ (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL ? 1 : 0);
if (len > pkt_len) {
return 0;
@ -627,7 +632,7 @@ ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
if (out == NULL) {
return 5 + 2 + pkt->dcid.len + pkt->scid.len
+ ngx_quic_varint_len(rem_len) + pkt->num_len
+ (pkt->level == ssl_encryption_initial ? 1 : 0);
+ (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL ? 1 : 0);
}
p = start = out;
@ -642,7 +647,7 @@ ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
*p++ = pkt->scid.len;
p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
if (pkt->level == ssl_encryption_initial) {
if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
ngx_quic_build_int(&p, 0);
}
@ -1768,7 +1773,7 @@ ngx_quic_parse_transport_params(u_char *p, u_char *end, ngx_quic_tp_t *tp,
}
if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, log, 0,
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
"quic %s transport param id:0x%xL, skipped",
(id % 31 == 27) ? "reserved" : "unknown", id);
}

View File

@ -47,9 +47,9 @@
(ngx_quic_long_pkt(flags) ? 0x0F : 0x1F)
#define ngx_quic_level_name(lvl) \
(lvl == ssl_encryption_application) ? "app" \
: (lvl == ssl_encryption_initial) ? "init" \
: (lvl == ssl_encryption_handshake) ? "hs" : "early"
(lvl == NGX_QUIC_ENCRYPTION_APPLICATION) ? "app" \
: (lvl == NGX_QUIC_ENCRYPTION_INITIAL) ? "init" \
: (lvl == NGX_QUIC_ENCRYPTION_HANDSHAKE) ? "hs" : "early"
#define NGX_QUIC_MAX_CID_LEN 20
#define NGX_QUIC_SERVER_CID_LEN NGX_QUIC_MAX_CID_LEN
@ -262,7 +262,7 @@ typedef struct ngx_quic_frame_s ngx_quic_frame_t;
struct ngx_quic_frame_s {
ngx_uint_t type;
enum ssl_encryption_level_t level;
ngx_uint_t level;
ngx_queue_t queue;
uint64_t pnum;
size_t plen;
@ -271,6 +271,7 @@ struct ngx_quic_frame_s {
unsigned need_ack:1;
unsigned pkt_need_ack:1;
unsigned ignore_congestion:1;
unsigned ignore_loss:1;
ngx_chain_t *data;
union {
@ -309,7 +310,7 @@ typedef struct {
uint8_t flags;
uint32_t version;
ngx_str_t token;
enum ssl_encryption_level_t level;
ngx_uint_t level;
ngx_uint_t error;
/* filled in by parser */

View File

@ -245,7 +245,7 @@ ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
&prev->types_keys, &prev->types,
ngx_http_html_default_types)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}

View File

@ -253,7 +253,8 @@ ngx_http_auth_basic_handler(ngx_http_request_t *r)
pwd.len = i - passwd;
pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
if (pwd.data == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
goto cleanup;
}
ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);

View File

@ -1332,6 +1332,12 @@ ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
table = ctx->table;
if (ctx->charset->utf8) {
if (value[1].len / 2 > NGX_UTF_LEN - 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
p = &table->src2dst[src * NGX_UTF_LEN];
*p++ = (u_char) (value[1].len / 2);
@ -1558,7 +1564,7 @@ ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
&prev->types_keys, &prev->types,
ngx_http_charset_default_types)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}

View File

@ -3130,7 +3130,7 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
prev->upstream.temp_path,
&ngx_http_fastcgi_temp_path)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}

View File

@ -205,6 +205,8 @@ static char *ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#if (NGX_HTTP_SSL)
static char *ngx_http_grpc_ssl_certificate_cache(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_grpc_ssl_password_file(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_grpc_ssl_conf_command_check(ngx_conf_t *cf, void *post,
@ -437,6 +439,13 @@ static ngx_command_t ngx_http_grpc_commands[] = {
offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_certificate_key),
NULL },
{ ngx_string("grpc_ssl_certificate_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_http_grpc_ssl_certificate_cache,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("grpc_ssl_password_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_grpc_ssl_password_file,
@ -1860,7 +1869,8 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (status < NGX_HTTP_OK) {
if (status < NGX_HTTP_OK && status != NGX_HTTP_EARLY_HINTS)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected :status \"%V\"",
status_line);
@ -1893,6 +1903,10 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
h->lowcase_key = h->key.data;
h->hash = ngx_hash_key(h->key.data, h->key.len);
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
continue;
}
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
@ -1914,6 +1928,17 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header done");
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
if (ctx->end_stream) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream prematurely closed stream");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
ctx->status = 0;
return NGX_HTTP_UPSTREAM_EARLY_HINTS;
}
if (ctx->end_stream) {
u->headers_in.content_length_n = 0;
@ -4386,6 +4411,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
conf->upstream.ssl_certificate = NGX_CONF_UNSET_PTR;
conf->upstream.ssl_certificate_key = NGX_CONF_UNSET_PTR;
conf->upstream.ssl_certificate_cache = NGX_CONF_UNSET_PTR;
conf->upstream.ssl_passwords = NGX_CONF_UNSET_PTR;
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
#endif
@ -4403,6 +4429,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
conf->upstream.pass_request_body = 1;
conf->upstream.force_ranges = 0;
conf->upstream.pass_trailers = 1;
conf->upstream.pass_early_hints = 1;
conf->upstream.preserve_output = 1;
conf->headers_source = NGX_CONF_UNSET_PTR;
@ -4497,8 +4524,15 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
prev->upstream.ssl_certificate, NULL);
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_key,
prev->upstream.ssl_certificate_key, NULL);
ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,
prev->upstream.ssl_passwords, NULL);
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_cache,
prev->upstream.ssl_certificate_cache, NULL);
if (ngx_http_upstream_merge_ssl_passwords(cf, &conf->upstream,
&prev->upstream)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
prev->ssl_conf_commands, NULL);
@ -4847,6 +4881,100 @@ ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#if (NGX_HTTP_SSL)
static char *
ngx_http_grpc_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_grpc_loc_conf_t *plcf = conf;
time_t inactive, valid;
ngx_str_t *value, s;
ngx_int_t max;
ngx_uint_t i;
if (plcf->upstream.ssl_certificate_cache != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
max = 0;
inactive = 10;
valid = 60;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
if (max <= 0) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
s.len = value[i].len - 9;
s.data = value[i].data + 9;
inactive = ngx_parse_time(&s, 1);
if (inactive == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
s.len = value[i].len - 6;
s.data = value[i].data + 6;
valid = ngx_parse_time(&s, 1);
if (valid == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strcmp(value[i].data, "off") == 0) {
plcf->upstream.ssl_certificate_cache = NULL;
continue;
}
failed:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (plcf->upstream.ssl_certificate_cache == NULL) {
return NGX_CONF_OK;
}
if (max == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"grpc_ssl_certificate_cache\" must have "
"the \"max\" parameter");
return NGX_CONF_ERROR;
}
plcf->upstream.ssl_certificate_cache = ngx_ssl_cache_init(cf->pool, max,
valid, inactive);
if (plcf->upstream.ssl_certificate_cache == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
@ -4971,16 +5099,9 @@ ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf)
return NGX_ERROR;
}
if (glcf->upstream.ssl_certificate->lengths
|| glcf->upstream.ssl_certificate_key->lengths)
if (glcf->upstream.ssl_certificate->lengths == NULL
&& glcf->upstream.ssl_certificate_key->lengths == NULL)
{
glcf->upstream.ssl_passwords =
ngx_ssl_preserve_passwords(cf, glcf->upstream.ssl_passwords);
if (glcf->upstream.ssl_passwords == NULL) {
return NGX_ERROR;
}
} else {
if (ngx_ssl_certificate(cf, glcf->upstream.ssl,
&glcf->upstream.ssl_certificate->value,
&glcf->upstream.ssl_certificate_key->value,

View File

@ -304,7 +304,7 @@ ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
{
int rc;
ctx->zstream.next_in = Z_NULL;
ctx->zstream.next_in = NULL;
ctx->zstream.avail_in = 0;
ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;

View File

@ -516,8 +516,10 @@ ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
/*
* Another zlib variant, https://github.com/zlib-ng/zlib-ng.
* It used to force window bits to 13 for fast compression level,
* uses (64 + sizeof(void*)) additional space on all allocations
* for alignment, 16-byte padding in one of window-sized buffers,
* used (64 + sizeof(void*)) additional space on all allocations
* for alignment and 16-byte padding in one of window-sized buffers,
* uses a single allocation with up to 200 bytes for alignment and
* internal pointers, 5/4 times more memory for the pending buffer,
* and 128K hash.
*/
@ -526,7 +528,7 @@ ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
}
ctx->allocated = 8192 + 16 + (1 << (wbits + 2))
+ 131072 + (1 << (memlevel + 8))
+ 131072 + (5 << (memlevel + 6))
+ 4 * (64 + sizeof(void*));
ctx->zlib_ng = 1;
}
@ -1113,7 +1115,7 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
&prev->types_keys, &prev->types,
ngx_http_html_default_types)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}

View File

@ -490,7 +490,7 @@ ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (value[i].len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"index \"%V\" in \"index\" directive is invalid",
&value[1]);
&value[i]);
return NGX_CONF_ERROR;
}

View File

@ -226,6 +226,8 @@ static char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#endif
#if (NGX_HTTP_SSL)
static char *ngx_http_proxy_ssl_certificate_cache(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_proxy_ssl_password_file(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
#endif
@ -775,6 +777,13 @@ static ngx_command_t ngx_http_proxy_commands[] = {
offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_certificate_key),
NULL },
{ ngx_string("proxy_ssl_certificate_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_http_proxy_ssl_certificate_cache,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("proxy_ssl_password_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_proxy_ssl_password_file,
@ -1879,6 +1888,13 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r)
u->headers_in.status_n, &u->headers_in.status_line);
if (ctx->status.http_version < NGX_HTTP_VERSION_11) {
if (ctx->status.code == NGX_HTTP_EARLY_HINTS) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent HTTP/1.0 response with early hints");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
u->headers_in.connection_close = 1;
}
@ -1940,6 +1956,14 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \"%V: %V\"",
&h->key, &h->value);
if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
continue;
}
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
@ -1951,10 +1975,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
}
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \"%V: %V\"",
&h->key, &h->value);
continue;
}
@ -1965,6 +1985,20 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header done");
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
ctx->status.code = 0;
ctx->status.count = 0;
ctx->status.start = NULL;
ctx->status.end = NULL;
r->upstream->process_header =
ngx_http_proxy_process_status_line;
r->state = 0;
return NGX_HTTP_UPSTREAM_EARLY_HINTS;
}
/*
* if no "Server" and "Date" in header line,
* then add the special empty headers
@ -2012,8 +2046,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
* connections alive in case of r->header_only or X-Accel-Redirect
*/
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
|| u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
|| ctx->head
@ -3613,15 +3645,16 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
conf->upstream.ssl_verify = NGX_CONF_UNSET;
conf->upstream.ssl_certificate = NGX_CONF_UNSET_PTR;
conf->upstream.ssl_certificate_key = NGX_CONF_UNSET_PTR;
conf->upstream.ssl_certificate_cache = NGX_CONF_UNSET_PTR;
conf->upstream.ssl_passwords = NGX_CONF_UNSET_PTR;
conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
#endif
/* "proxy_cyclic_temp_file" is disabled */
/* the hardcoded values */
conf->upstream.cyclic_temp_file = 0;
conf->upstream.change_buffering = 1;
conf->upstream.pass_early_hints = 1;
conf->headers_source = NGX_CONF_UNSET_PTR;
@ -3834,7 +3867,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
prev->upstream.temp_path,
&ngx_http_proxy_temp_path)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}
@ -3964,8 +3997,15 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
prev->upstream.ssl_certificate, NULL);
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_key,
prev->upstream.ssl_certificate_key, NULL);
ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,
prev->upstream.ssl_passwords, NULL);
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_cache,
prev->upstream.ssl_certificate_cache, NULL);
if (ngx_http_upstream_merge_ssl_passwords(cf, &conf->upstream,
&prev->upstream)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
prev->ssl_conf_commands, NULL);
@ -5074,6 +5114,100 @@ ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#if (NGX_HTTP_SSL)
static char *
ngx_http_proxy_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_proxy_loc_conf_t *plcf = conf;
time_t inactive, valid;
ngx_str_t *value, s;
ngx_int_t max;
ngx_uint_t i;
if (plcf->upstream.ssl_certificate_cache != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
max = 0;
inactive = 10;
valid = 60;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
if (max <= 0) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
s.len = value[i].len - 9;
s.data = value[i].data + 9;
inactive = ngx_parse_time(&s, 1);
if (inactive == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
s.len = value[i].len - 6;
s.data = value[i].data + 6;
valid = ngx_parse_time(&s, 1);
if (valid == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strcmp(value[i].data, "off") == 0) {
plcf->upstream.ssl_certificate_cache = NULL;
continue;
}
failed:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (plcf->upstream.ssl_certificate_cache == NULL) {
return NGX_CONF_OK;
}
if (max == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_ssl_certificate_cache\" must have "
"the \"max\" parameter");
return NGX_CONF_ERROR;
}
plcf->upstream.ssl_certificate_cache = ngx_ssl_cache_init(cf->pool, max,
valid, inactive);
if (plcf->upstream.ssl_certificate_cache == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
@ -5231,16 +5365,9 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)
return NGX_ERROR;
}
if (plcf->upstream.ssl_certificate->lengths
|| plcf->upstream.ssl_certificate_key->lengths)
if (plcf->upstream.ssl_certificate->lengths == NULL
&& plcf->upstream.ssl_certificate_key->lengths == NULL)
{
plcf->upstream.ssl_passwords =
ngx_ssl_preserve_passwords(cf, plcf->upstream.ssl_passwords);
if (plcf->upstream.ssl_passwords == NULL) {
return NGX_ERROR;
}
} else {
if (ngx_ssl_certificate(cf, plcf->upstream.ssl,
&plcf->upstream.ssl_certificate->value,
&plcf->upstream.ssl_certificate_key->value,

View File

@ -1543,7 +1543,7 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
prev->upstream.temp_path,
&ngx_http_scgi_temp_path)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}

View File

@ -165,8 +165,8 @@ ngx_http_slice_header_filter(ngx_http_request_t *r)
if (cr.start != ctx->start || cr.end != end) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"unexpected range in slice response: %O-%O",
cr.start, cr.end);
"unexpected range in slice response: %O-%O, "
"expected: %O-%O", cr.start, cr.end, ctx->start, end);
return NGX_ERROR;
}
@ -419,13 +419,13 @@ ngx_http_slice_range_variable(ngx_http_request_t *r,
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
p = ngx_pnalloc(r->pool, sizeof("bytes=-") - 1 + 2 * NGX_OFF_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size);
ctx->range.data = p;

View File

@ -820,7 +820,7 @@ ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
}
for (prm = cmd->params; prm->name.len; prm++) {
if (prm->mandatory && params[prm->index] == 0) {
if (prm->mandatory && params[prm->index] == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"mandatory \"%V\" parameter is absent "
"in \"%V\" SSI command",
@ -2942,7 +2942,7 @@ ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
&prev->types_keys, &prev->types,
ngx_http_html_default_types)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}

View File

@ -43,6 +43,8 @@ static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
static ngx_int_t ngx_http_ssl_compile_certificates(ngx_conf_t *cf,
ngx_http_ssl_srv_conf_t *conf);
static char *ngx_http_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
@ -108,6 +110,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
offsetof(ngx_http_ssl_srv_conf_t, certificate_keys),
NULL },
{ ngx_string("ssl_certificate_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE123,
ngx_http_ssl_certificate_cache,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("ssl_password_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_ssl_password_file,
@ -115,6 +124,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
0,
NULL },
{ ngx_string("ssl_certificate_compression"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, certificate_compression),
NULL },
{ ngx_string("ssl_dhparam"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
@ -612,6 +628,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
*/
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
sscf->certificate_compression = NGX_CONF_UNSET;
sscf->early_data = NGX_CONF_UNSET;
sscf->reject_handshake = NGX_CONF_UNSET;
sscf->buffer_size = NGX_CONF_UNSET_SIZE;
@ -619,6 +636,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
sscf->verify_depth = NGX_CONF_UNSET_UINT;
sscf->certificates = NGX_CONF_UNSET_PTR;
sscf->certificate_keys = NGX_CONF_UNSET_PTR;
sscf->certificate_cache = NGX_CONF_UNSET_PTR;
sscf->passwords = NGX_CONF_UNSET_PTR;
sscf->conf_commands = NGX_CONF_UNSET_PTR;
sscf->builtin_session_cache = NGX_CONF_UNSET;
@ -648,6 +666,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->prefer_server_ciphers,
prev->prefer_server_ciphers, 0);
ngx_conf_merge_value(conf->certificate_compression,
prev->certificate_compression, 0);
ngx_conf_merge_value(conf->early_data, prev->early_data, 0);
ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0);
@ -664,6 +685,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
NULL);
ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache,
NULL);
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
@ -725,6 +749,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
cln->data = &conf->ssl;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
{
static ngx_ssl_client_hello_arg cb = { ngx_http_ssl_servername };
ngx_ssl_set_client_hello_callback(conf->ssl.ctx, &cb);
if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
ngx_http_ssl_servername)
@ -735,7 +763,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
"dynamically to an OpenSSL library which has no tlsext support, "
"therefore SNI is not available");
}
}
#endif
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
@ -779,6 +807,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
return NGX_CONF_ERROR;
}
if (ngx_ssl_certificate_compression(cf, &conf->ssl,
conf->certificate_compression)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
conf->ssl.buffer_size = conf->buffer_size;
@ -875,13 +910,19 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->stapling) {
if (conf->certificate_compression) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"\"ssl_stapling\" is incompatible with "
"\"ssl_certificate_compression\"");
return NGX_CONF_ERROR;
}
if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
&conf->stapling_responder, conf->stapling_verify)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
if (ngx_ssl_early_data(cf, &conf->ssl, conf->early_data) != NGX_OK) {
@ -984,6 +1025,99 @@ found:
}
static char *
ngx_http_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_ssl_srv_conf_t *sscf = conf;
time_t inactive, valid;
ngx_str_t *value, s;
ngx_int_t max;
ngx_uint_t i;
if (sscf->certificate_cache != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
max = 0;
inactive = 10;
valid = 60;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
if (max <= 0) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
s.len = value[i].len - 9;
s.data = value[i].data + 9;
inactive = ngx_parse_time(&s, 1);
if (inactive == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
s.len = value[i].len - 6;
s.data = value[i].data + 6;
valid = ngx_parse_time(&s, 1);
if (valid == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strcmp(value[i].data, "off") == 0) {
sscf->certificate_cache = NULL;
continue;
}
failed:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (sscf->certificate_cache == NULL) {
return NGX_CONF_OK;
}
if (max == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"ssl_certificate_cache\" must have "
"the \"max\" parameter");
return NGX_CONF_ERROR;
}
sscf->certificate_cache = ngx_ssl_cache_init(cf->pool, max, valid,
inactive);
if (sscf->certificate_cache == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{

View File

@ -18,6 +18,7 @@ typedef struct {
ngx_ssl_t ssl;
ngx_flag_t prefer_server_ciphers;
ngx_flag_t certificate_compression;
ngx_flag_t early_data;
ngx_flag_t reject_handshake;
@ -38,6 +39,8 @@ typedef struct {
ngx_array_t *certificate_values;
ngx_array_t *certificate_key_values;
ngx_ssl_cache_t *certificate_cache;
ngx_str_t dhparam;
ngx_str_t ecdh_curve;
ngx_str_t client_certificate;

View File

@ -901,7 +901,7 @@ ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
&prev->types_keys, &prev->types,
ngx_http_html_default_types)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}

View File

@ -92,6 +92,8 @@ static char *ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
#endif
#if (NGX_HTTP_SSL)
static char *ngx_http_uwsgi_ssl_certificate_cache(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_uwsgi_ssl_password_file(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_uwsgi_ssl_conf_command_check(ngx_conf_t *cf, void *post,
@ -559,6 +561,13 @@ static ngx_command_t ngx_http_uwsgi_commands[] = {
offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_certificate_key),
NULL },
{ ngx_string("uwsgi_ssl_certificate_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_http_uwsgi_ssl_certificate_cache,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("uwsgi_ssl_password_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_uwsgi_ssl_password_file,
@ -1590,6 +1599,7 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf)
conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
conf->upstream.ssl_certificate = NGX_CONF_UNSET_PTR;
conf->upstream.ssl_certificate_key = NGX_CONF_UNSET_PTR;
conf->upstream.ssl_certificate_cache = NGX_CONF_UNSET_PTR;
conf->upstream.ssl_passwords = NGX_CONF_UNSET_PTR;
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
#endif
@ -1793,7 +1803,7 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
prev->upstream.temp_path,
&ngx_http_uwsgi_temp_path)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}
@ -1921,8 +1931,15 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
prev->upstream.ssl_certificate, NULL);
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_key,
prev->upstream.ssl_certificate_key, NULL);
ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,
prev->upstream.ssl_passwords, NULL);
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_cache,
prev->upstream.ssl_certificate_cache, NULL);
if (ngx_http_upstream_merge_ssl_passwords(cf, &conf->upstream,
&prev->upstream)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
prev->ssl_conf_commands, NULL);
@ -2455,6 +2472,100 @@ ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#if (NGX_HTTP_SSL)
static char *
ngx_http_uwsgi_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_uwsgi_loc_conf_t *plcf = conf;
time_t inactive, valid;
ngx_str_t *value, s;
ngx_int_t max;
ngx_uint_t i;
if (plcf->upstream.ssl_certificate_cache != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
max = 0;
inactive = 10;
valid = 60;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
if (max <= 0) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
s.len = value[i].len - 9;
s.data = value[i].data + 9;
inactive = ngx_parse_time(&s, 1);
if (inactive == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
s.len = value[i].len - 6;
s.data = value[i].data + 6;
valid = ngx_parse_time(&s, 1);
if (valid == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strcmp(value[i].data, "off") == 0) {
plcf->upstream.ssl_certificate_cache = NULL;
continue;
}
failed:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (plcf->upstream.ssl_certificate_cache == NULL) {
return NGX_CONF_OK;
}
if (max == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"uwsgi_ssl_certificate_cache\" must have "
"the \"max\" parameter");
return NGX_CONF_ERROR;
}
plcf->upstream.ssl_certificate_cache = ngx_ssl_cache_init(cf->pool, max,
valid, inactive);
if (plcf->upstream.ssl_certificate_cache == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_uwsgi_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
@ -2579,16 +2690,9 @@ ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf)
return NGX_ERROR;
}
if (uwcf->upstream.ssl_certificate->lengths
|| uwcf->upstream.ssl_certificate_key->lengths)
if (uwcf->upstream.ssl_certificate->lengths == NULL
&& uwcf->upstream.ssl_certificate_key->lengths == NULL)
{
uwcf->upstream.ssl_passwords =
ngx_ssl_preserve_passwords(cf, uwcf->upstream.ssl_passwords);
if (uwcf->upstream.ssl_passwords == NULL) {
return NGX_ERROR;
}
} else {
if (ngx_ssl_certificate(cf, uwcf->upstream.ssl,
&uwcf->upstream.ssl_certificate->value,
&uwcf->upstream.ssl_certificate_key->value,

View File

@ -1112,7 +1112,7 @@ ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
&prev->types_keys, &prev->types,
ngx_http_xslt_default_types)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}

View File

@ -72,6 +72,7 @@ ngx_uint_t ngx_http_max_module;
ngx_http_output_header_filter_pt ngx_http_top_header_filter;
ngx_http_output_header_filter_pt ngx_http_top_early_hints_filter;
ngx_http_output_body_filter_pt ngx_http_top_body_filter;
ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;

View File

@ -122,7 +122,6 @@ ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c);
ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r);
ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
void ngx_http_process_request(ngx_http_request_t *r);
void ngx_http_update_location_config(ngx_http_request_t *r);
void ngx_http_handler(ngx_http_request_t *r);
@ -152,6 +151,7 @@ ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,
ngx_int_t ngx_http_read_unbuffered_request_body(ngx_http_request_t *r);
ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
ngx_int_t ngx_http_send_early_hints(ngx_http_request_t *r);
ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
ngx_int_t error);
ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
@ -191,6 +191,7 @@ extern ngx_str_t ngx_http_html_default_types[];
extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
extern ngx_http_output_header_filter_pt ngx_http_top_early_hints_filter;
extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
extern ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;

View File

@ -509,6 +509,13 @@ static ngx_command_t ngx_http_core_commands[] = {
0,
NULL },
{ ngx_string("keepalive_min_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_core_loc_conf_t, keepalive_min_timeout),
NULL },
{ ngx_string("keepalive_requests"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
@ -663,6 +670,13 @@ static ngx_command_t ngx_http_core_commands[] = {
offsetof(ngx_http_core_loc_conf_t, etag),
NULL },
{ ngx_string("early_hints"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_set_predicate_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_core_loc_conf_t, early_hints),
NULL },
{ ngx_string("error_page"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_2MORE,
@ -1850,6 +1864,37 @@ ngx_http_send_header(ngx_http_request_t *r)
}
ngx_int_t
ngx_http_send_early_hints(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_core_loc_conf_t *clcf;
if (r->post_action) {
return NGX_OK;
}
if (r->header_sent) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"header already sent");
return NGX_ERROR;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
rc = ngx_http_test_predicates(r, clcf->early_hints);
if (rc != NGX_DECLINED) {
return rc;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http send early hints \"%V?%V\"", &r->uri, &r->args);
return ngx_http_top_early_hints_filter(r);
}
ngx_int_t
ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
@ -2320,6 +2365,7 @@ ngx_http_subrequest(ngx_http_request_t *r,
ngx_connection_t *c;
ngx_http_request_t *sr;
ngx_http_core_srv_conf_t *cscf;
ngx_http_posted_request_t *posted;
ngx_http_postponed_request_t *pr, *p;
if (r->subrequests == 0) {
@ -2373,6 +2419,11 @@ ngx_http_subrequest(ngx_http_request_t *r,
return NGX_ERROR;
}
posted = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
if (posted == NULL) {
return NGX_ERROR;
}
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
sr->main_conf = cscf->ctx->main_conf;
sr->srv_conf = cscf->ctx->srv_conf;
@ -2431,10 +2482,6 @@ ngx_http_subrequest(ngx_http_request_t *r,
}
if (!sr->background) {
if (c->data == r && r->postponed == NULL) {
c->data = sr;
}
pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
if (pr == NULL) {
return NGX_ERROR;
@ -2444,6 +2491,10 @@ ngx_http_subrequest(ngx_http_request_t *r,
pr->out = NULL;
pr->next = NULL;
if (c->data == r && r->postponed == NULL) {
c->data = sr;
}
if (r->postponed) {
for (p = r->postponed; p->next; p = p->next) { /* void */ }
p->next = pr;
@ -2491,7 +2542,7 @@ ngx_http_subrequest(ngx_http_request_t *r,
ngx_http_update_location_config(sr);
}
return ngx_http_post_request(sr, NULL);
return ngx_http_post_request(sr, posted);
}
@ -3606,6 +3657,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
clcf->keepalive_time = NGX_CONF_UNSET_MSEC;
clcf->keepalive_timeout = NGX_CONF_UNSET_MSEC;
clcf->keepalive_header = NGX_CONF_UNSET;
clcf->keepalive_min_timeout = NGX_CONF_UNSET_MSEC;
clcf->keepalive_requests = NGX_CONF_UNSET_UINT;
clcf->lingering_close = NGX_CONF_UNSET_UINT;
clcf->lingering_time = NGX_CONF_UNSET_MSEC;
@ -3623,6 +3675,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
clcf->chunked_transfer_encoding = NGX_CONF_UNSET;
clcf->etag = NGX_CONF_UNSET;
clcf->server_tokens = NGX_CONF_UNSET_UINT;
clcf->early_hints = NGX_CONF_UNSET_PTR;
clcf->types_hash_max_size = NGX_CONF_UNSET_UINT;
clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;
@ -3844,6 +3897,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
prev->keepalive_timeout, 75000);
ngx_conf_merge_sec_value(conf->keepalive_header,
prev->keepalive_header, 0);
ngx_conf_merge_msec_value(conf->keepalive_min_timeout,
prev->keepalive_min_timeout, 0);
ngx_conf_merge_uint_value(conf->keepalive_requests,
prev->keepalive_requests, 1000);
ngx_conf_merge_uint_value(conf->lingering_close,
@ -3876,7 +3931,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path,
prev->client_body_temp_path,
&ngx_http_client_temp_path)
!= NGX_OK)
!= NGX_CONF_OK)
{
return NGX_CONF_ERROR;
}
@ -3901,6 +3956,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_uint_value(conf->server_tokens, prev->server_tokens,
NGX_HTTP_SERVER_TOKENS_ON);
ngx_conf_merge_ptr_value(conf->early_hints, prev->early_hints, NULL);
ngx_conf_merge_ptr_value(conf->open_file_cache,
prev->open_file_cache, NULL);

View File

@ -370,6 +370,7 @@ struct ngx_http_core_loc_conf_s {
ngx_msec_t send_timeout; /* send_timeout */
ngx_msec_t keepalive_time; /* keepalive_time */
ngx_msec_t keepalive_timeout; /* keepalive_timeout */
ngx_msec_t keepalive_min_timeout; /* keepalive_min_timeout */
ngx_msec_t lingering_time; /* lingering_time */
ngx_msec_t lingering_timeout; /* lingering_timeout */
ngx_msec_t resolver_timeout; /* resolver_timeout */
@ -429,6 +430,8 @@ struct ngx_http_core_loc_conf_s {
ngx_http_complex_value_t *disable_symlinks_from;
#endif
ngx_array_t *early_hints; /* early_hints */
ngx_array_t *error_pages; /* error_page */
ngx_path_t *client_body_temp_path; /* client_body_temp_path */

View File

@ -13,6 +13,7 @@
static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_early_hints_filter(ngx_http_request_t *r);
static ngx_http_module_t ngx_http_header_filter_module_ctx = {
@ -50,6 +51,9 @@ static u_char ngx_http_server_string[] = "Server: nginx" CRLF;
static u_char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
static u_char ngx_http_server_build_string[] = "Server: " NGINX_VER_BUILD CRLF;
static ngx_str_t ngx_http_early_hints_status_line =
ngx_string("HTTP/1.1 103 Early Hints" CRLF);
static ngx_str_t ngx_http_status_lines[] = {
@ -625,10 +629,113 @@ ngx_http_header_filter(ngx_http_request_t *r)
}
static ngx_int_t
ngx_http_early_hints_filter(ngx_http_request_t *r)
{
size_t len;
ngx_buf_t *b;
ngx_uint_t i;
ngx_chain_t out;
ngx_list_part_t *part;
ngx_table_elt_t *header;
if (r != r->main) {
return NGX_OK;
}
if (r->http_version < NGX_HTTP_VERSION_11) {
return NGX_OK;
}
len = 0;
part = &r->headers_out.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len
+ sizeof(CRLF) - 1;
}
if (len == 0) {
return NGX_OK;
}
len += ngx_http_early_hints_status_line.len
/* the end of the early hints */
+ sizeof(CRLF) - 1;
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_ERROR;
}
b->last = ngx_copy(b->last, ngx_http_early_hints_status_line.data,
ngx_http_early_hints_status_line.len);
part = &r->headers_out.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
*b->last++ = ':'; *b->last++ = ' ';
b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
*b->last++ = CR; *b->last++ = LF;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"%*s", (size_t) (b->last - b->pos), b->pos);
/* the end of HTTP early hints */
*b->last++ = CR; *b->last++ = LF;
r->header_size = b->last - b->pos;
b->flush = 1;
out.buf = b;
out.next = NULL;
return ngx_http_write_filter(r, &out);
}
static ngx_int_t
ngx_http_header_filter_init(ngx_conf_t *cf)
{
ngx_http_top_header_filter = ngx_http_header_filter;
ngx_http_top_early_hints_filter = ngx_http_early_hints_filter;
return NGX_OK;
}

View File

@ -29,6 +29,7 @@ static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,
ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);
@ -890,8 +891,27 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
if (c->ssl->sni_accepted) {
return SSL_TLSEXT_ERR_OK;
}
if (c->ssl->handshake_rejected) {
*ad = SSL_AD_UNRECOGNIZED_NAME;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
hc = c->data;
if (arg != NULL) {
host = *(ngx_str_t *) arg;
if (host.data == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL server name: null");
goto done;
}
} else {
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
if (servername == NULL) {
@ -900,17 +920,17 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
goto done;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL server name: \"%s\"", servername);
host.len = ngx_strlen(servername);
host.data = (u_char *) servername;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL server name: \"%V\"", &host);
if (host.len == 0) {
goto done;
}
host.data = (u_char *) servername;
rc = ngx_http_validate_host(&host, c->pool, 1);
if (rc == NGX_ERROR) {
@ -945,7 +965,7 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
ngx_set_connection_log(c, clcf->error_log);
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);
c->ssl->buffer_size = sscf->buffer_size;
@ -995,6 +1015,7 @@ done:
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
c->ssl->sni_accepted = 1;
return SSL_TLSEXT_ERR_OK;
error:
@ -1054,6 +1075,7 @@ ngx_http_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg)
"ssl key: \"%s\"", key.data);
if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key,
sscf->certificate_cache,
sscf->passwords)
!= NGX_OK)
{
@ -1960,7 +1982,7 @@ ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,
}
ngx_int_t
static ngx_int_t
ngx_http_process_request_header(ngx_http_request_t *r)
{
if (r->headers_in.server.len == 0
@ -2798,6 +2820,13 @@ ngx_http_finalize_connection(ngx_http_request_t *r)
r->lingering_close = 1;
}
if (r->keepalive
&& clcf->keepalive_min_timeout > 0)
{
ngx_http_set_keepalive(r);
return;
}
if (!ngx_terminate
&& !ngx_exiting
&& r->keepalive
@ -3300,10 +3329,22 @@ ngx_http_set_keepalive(ngx_http_request_t *r)
r->http_state = NGX_HTTP_KEEPALIVE_STATE;
#endif
if (clcf->keepalive_min_timeout == 0) {
c->idle = 1;
ngx_reusable_connection(c, 1);
}
ngx_add_timer(rev, clcf->keepalive_timeout);
if (clcf->keepalive_min_timeout > 0
&& clcf->keepalive_timeout > clcf->keepalive_min_timeout)
{
hc->keepalive_timeout = clcf->keepalive_timeout
- clcf->keepalive_min_timeout;
} else {
hc->keepalive_timeout = 0;
}
ngx_add_timer(rev, clcf->keepalive_timeout - hc->keepalive_timeout);
if (rev->ready) {
ngx_post_event(rev, &ngx_posted_events);
@ -3318,11 +3359,28 @@ ngx_http_keepalive_handler(ngx_event_t *rev)
ssize_t n;
ngx_buf_t *b;
ngx_connection_t *c;
ngx_http_connection_t *hc;
c = rev->data;
hc = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http keepalive handler");
if (!ngx_terminate
&& !ngx_exiting
&& rev->timedout
&& hc->keepalive_timeout > 0)
{
c->idle = 1;
ngx_reusable_connection(c, 1);
ngx_add_timer(rev, hc->keepalive_timeout);
hc->keepalive_timeout = 0;
rev->timedout = 0;
return;
}
if (rev->timedout || c->close) {
ngx_http_close_connection(c);
return;

View File

@ -74,6 +74,7 @@
#define NGX_HTTP_CONTINUE 100
#define NGX_HTTP_SWITCHING_PROTOCOLS 101
#define NGX_HTTP_PROCESSING 102
#define NGX_HTTP_EARLY_HINTS 103
#define NGX_HTTP_OK 200
#define NGX_HTTP_CREATED 201
@ -329,6 +330,8 @@ typedef struct {
ngx_chain_t *free;
ngx_msec_t keepalive_timeout;
unsigned ssl:1;
unsigned proxy_protocol:1;
} ngx_http_connection_t;

View File

@ -48,6 +48,9 @@ static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
static void ngx_http_upstream_read_request_handler(ngx_http_request_t *r);
static void ngx_http_upstream_process_header(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_early_hints_writer(ngx_http_request_t *r);
static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
@ -113,6 +116,10 @@ static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,
static ngx_int_t
ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
#if (NGX_HTTP_CACHE)
static ngx_int_t ngx_http_upstream_process_delta_seconds(u_char *p,
u_char *last);
#endif
static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,
@ -1120,7 +1127,7 @@ ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
return NGX_ERROR;
}
if (rc == NGX_AGAIN) {
if (rc == NGX_AGAIN || rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) {
rc = NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
@ -2019,6 +2026,7 @@ ngx_http_upstream_ssl_certificate(ngx_http_request_t *r,
"http upstream ssl key: \"%s\"", key.data);
if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key,
u->conf->ssl_certificate_cache,
u->conf->ssl_passwords)
!= NGX_OK)
{
@ -2529,6 +2537,18 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
continue;
}
if (rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) {
rc = ngx_http_upstream_process_early_hints(r, u);
if (rc == NGX_OK) {
rc = u->process_header(r);
if (rc == NGX_AGAIN) {
continue;
}
}
}
break;
}
@ -2566,6 +2586,148 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
}
static ngx_int_t
ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
u_char *p;
ngx_uint_t i;
ngx_list_part_t *part;
ngx_table_elt_t *h, *ho;
ngx_connection_t *c;
c = r->connection;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream early hints");
if (u->conf->pass_early_hints) {
u->early_hints_length += u->buffer.pos - u->buffer.start;
if (u->early_hints_length <= (off_t) u->conf->buffer_size) {
part = &u->headers_in.headers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
h[i].lowcase_key, h[i].key.len))
{
continue;
}
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = h[i];
}
if (ngx_http_send_early_hints(r) == NGX_ERROR) {
return NGX_ERROR;
}
if (c->buffered) {
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
return NGX_ERROR;
}
r->write_event_handler = ngx_http_upstream_early_hints_writer;
}
} else {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"upstream sent too big early hints");
}
}
ngx_http_clean_header(r);
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
u->headers_in.content_length_n = -1;
u->headers_in.last_modified_time = -1;
if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
return NGX_ERROR;
}
p = u->buffer.pos;
u->buffer.pos = u->buffer.start;
#if (NGX_HTTP_CACHE)
if (r->cache) {
u->buffer.pos += r->cache->header_start;
}
#endif
u->buffer.last = ngx_movemem(u->buffer.pos, p, u->buffer.last - p);
return NGX_OK;
}
static void
ngx_http_upstream_early_hints_writer(ngx_http_request_t *r)
{
ngx_connection_t *c;
ngx_http_upstream_t *u;
c = r->connection;
u = r->upstream;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream early hints writer");
c->log->action = "sending early hints to client";
if (ngx_http_write_filter(r, NULL) == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (!c->buffered) {
if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
r->write_event_handler =
ngx_http_upstream_wr_check_broken_connection;
} else {
r->write_event_handler = ngx_http_request_empty_handler;
}
}
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
}
}
static ngx_int_t
ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
@ -4908,18 +5070,9 @@ ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
}
if (p) {
n = 0;
for (p += offset; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p >= '0' && *p <= '9') {
n = n * 10 + (*p - '0');
continue;
}
n = ngx_http_upstream_process_delta_seconds(p + offset, last);
if (n == NGX_ERROR) {
u->cacheable = 0;
return NGX_OK;
}
@ -4929,7 +5082,8 @@ ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
return NGX_OK;
}
r->cache->valid_sec = ngx_time() + n;
r->cache->valid_sec = ngx_min((ngx_uint_t) ngx_time() + n,
NGX_MAX_INT_T_VALUE);
u->headers_in.expired = 0;
}
@ -4939,18 +5093,9 @@ extensions:
23 - 1);
if (p) {
n = 0;
for (p += 23; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p >= '0' && *p <= '9') {
n = n * 10 + (*p - '0');
continue;
}
n = ngx_http_upstream_process_delta_seconds(p + 23, last);
if (n == NGX_ERROR) {
u->cacheable = 0;
return NGX_OK;
}
@ -4962,18 +5107,9 @@ extensions:
p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1);
if (p) {
n = 0;
for (p += 15; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p >= '0' && *p <= '9') {
n = n * 10 + (*p - '0');
continue;
}
n = ngx_http_upstream_process_delta_seconds(p + 15, last);
if (n == NGX_ERROR) {
u->cacheable = 0;
return NGX_OK;
}
@ -4987,6 +5123,41 @@ extensions:
}
#if (NGX_HTTP_CACHE)
static ngx_int_t
ngx_http_upstream_process_delta_seconds(u_char *p, u_char *last)
{
ngx_int_t n, cutoff, cutlim;
cutoff = NGX_MAX_INT_T_VALUE / 10;
cutlim = NGX_MAX_INT_T_VALUE % 10;
n = 0;
for ( /* void */ ; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p < '0' || *p > '9') {
return NGX_ERROR;
}
if (n >= cutoff && (n > cutoff || *p - '0' > cutlim)) {
n = NGX_MAX_INT_T_VALUE;
break;
}
n = n * 10 + (*p - '0');
}
return n;
}
#endif
static ngx_int_t
ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
@ -6920,6 +7091,61 @@ ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
}
#if (NGX_HTTP_SSL)
ngx_int_t
ngx_http_upstream_merge_ssl_passwords(ngx_conf_t *cf,
ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev)
{
ngx_uint_t preserve;
ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
if (conf->ssl_certificate == NULL
|| conf->ssl_certificate->value.len == 0
|| conf->ssl_certificate_key == NULL)
{
return NGX_OK;
}
if (conf->ssl_certificate->lengths == NULL
&& conf->ssl_certificate_key->lengths == NULL)
{
if (conf->ssl_passwords && conf->ssl_passwords->pool == NULL) {
/* un-preserve empty password list */
conf->ssl_passwords = NULL;
}
return NGX_OK;
}
if (conf->ssl_passwords && conf->ssl_passwords->pool != cf->temp_pool) {
/* already preserved */
return NGX_OK;
}
preserve = (conf->ssl_passwords == prev->ssl_passwords) ? 1 : 0;
conf->ssl_passwords = ngx_ssl_preserve_passwords(cf, conf->ssl_passwords);
if (conf->ssl_passwords == NULL) {
return NGX_ERROR;
}
/*
* special handling to keep a preserved ssl_passwords copy
* in the previous configuration to inherit it to all children
*/
if (preserve) {
prev->ssl_passwords = conf->ssl_passwords;
}
return NGX_OK;
}
#endif
static void *
ngx_http_upstream_create_main_conf(ngx_conf_t *cf)
{

View File

@ -43,6 +43,7 @@
|NGX_HTTP_UPSTREAM_FT_HTTP_429)
#define NGX_HTTP_UPSTREAM_INVALID_HEADER 40
#define NGX_HTTP_UPSTREAM_EARLY_HINTS 41
#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT 0x00000002
@ -185,6 +186,7 @@ typedef struct {
ngx_flag_t pass_request_headers;
ngx_flag_t pass_request_body;
ngx_flag_t pass_trailers;
ngx_flag_t pass_early_hints;
ngx_flag_t ignore_client_abort;
ngx_flag_t intercept_errors;
@ -245,6 +247,7 @@ typedef struct {
ngx_http_complex_value_t *ssl_certificate;
ngx_http_complex_value_t *ssl_certificate_key;
ngx_ssl_cache_t *ssl_certificate_cache;
ngx_array_t *ssl_passwords;
#endif
@ -353,6 +356,7 @@ struct ngx_http_upstream_s {
ngx_buf_t buffer;
off_t length;
off_t early_hints_length;
ngx_chain_t *out_bufs;
ngx_chain_t *busy_bufs;
@ -436,6 +440,10 @@ char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
ngx_str_t *default_hide_headers, ngx_hash_init_t *hash);
#if (NGX_HTTP_SSL)
ngx_int_t ngx_http_upstream_merge_ssl_passwords(ngx_conf_t *cf,
ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev);
#endif
#define ngx_http_conf_upstream_srv_conf(uscf, module) \

View File

@ -878,7 +878,6 @@ ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
int len;
const u_char *p;
ngx_http_upstream_rr_peers_t *peers;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
#endif
peer = rrp->current;
@ -898,12 +897,12 @@ ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
len = peer->ssl_session_len;
ngx_memcpy(buf, peer->ssl_session, len);
ngx_memcpy(ngx_ssl_session_buffer, peer->ssl_session, len);
ngx_http_upstream_rr_peer_unlock(peers, peer);
ngx_http_upstream_rr_peers_unlock(peers);
p = buf;
p = ngx_ssl_session_buffer;
ssl_session = d2i_SSL_SESSION(NULL, &p, len);
rc = ngx_ssl_set_session(pc->connection, ssl_session);
@ -940,7 +939,6 @@ ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
int len;
u_char *p;
ngx_http_upstream_rr_peers_t *peers;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
#endif
#if (NGX_HTTP_UPSTREAM_ZONE)
@ -954,18 +952,18 @@ ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
return;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"save session: %p", ssl_session);
len = i2d_SSL_SESSION(ssl_session, NULL);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"save session: %p:%d", ssl_session, len);
/* do not cache too big session */
if (len > NGX_SSL_MAX_SESSION_SIZE) {
return;
}
p = buf;
p = ngx_ssl_session_buffer;
(void) i2d_SSL_SESSION(ssl_session, &p);
peer = rrp->current;
@ -995,7 +993,7 @@ ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
peer->ssl_session_len = len;
}
ngx_memcpy(peer->ssl_session, buf, len);
ngx_memcpy(peer->ssl_session, ngx_ssl_session_buffer, len);
ngx_http_upstream_rr_peer_unlock(peers, peer);
ngx_http_upstream_rr_peers_unlock(peers);
@ -1023,8 +1021,6 @@ ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"old session: %p", old_ssl_session);
/* TODO: may block */
ngx_ssl_free_session(old_ssl_session);
}
}

View File

@ -158,6 +158,7 @@ static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r,
ngx_http_v2_header_t *header);
static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_construct_host_header(ngx_http_request_t *r);
static void ngx_http_v2_run_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r,
u_char *pos, size_t size, ngx_uint_t last, ngx_uint_t flush);
@ -3517,44 +3518,40 @@ ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)
static ngx_int_t
ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value)
{
ngx_table_elt_t *h;
ngx_http_header_t *hh;
ngx_http_core_main_conf_t *cmcf;
ngx_int_t rc;
static ngx_str_t host = ngx_string("host");
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
if (r->host_start) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent duplicate \":authority\" header");
return NGX_DECLINED;
}
h->hash = ngx_hash(ngx_hash(ngx_hash('h', 'o'), 's'), 't');
r->host_start = value->data;
r->host_end = value->data + value->len;
h->key.len = host.len;
h->key.data = host.data;
rc = ngx_http_validate_host(value, r->pool, 0);
h->value.len = value->len;
h->value.data = value->data;
h->lowcase_key = host.data;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh == NULL) {
return NGX_ERROR;
if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid \":authority\" header");
return NGX_DECLINED;
}
if (hh->handler(r, h, hh->offset) != NGX_OK) {
if (rc == NGX_ERROR) {
ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ABORT;
}
if (ngx_http_set_virtual_server(r, value) == NGX_ERROR) {
/*
* request has been finalized already
* in ngx_http_process_host()
* in ngx_http_set_virtual_server()
*/
return NGX_ABORT;
}
r->headers_in.server = *value;
return NGX_OK;
}
@ -3725,7 +3722,54 @@ ngx_http_v2_construct_cookie_header(ngx_http_request_t *r)
if (hh->handler(r, h, hh->offset) != NGX_OK) {
/*
* request has been finalized already
* in ngx_http_process_multi_header_lines()
* in ngx_http_process_header_line()
*/
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_construct_host_header(ngx_http_request_t *r)
{
ngx_table_elt_t *h;
ngx_http_header_t *hh;
ngx_http_core_main_conf_t *cmcf;
static ngx_str_t host = ngx_string("host");
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL) {
ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
h->hash = ngx_hash(ngx_hash(ngx_hash('h', 'o'), 's'), 't');
h->key.len = host.len;
h->key.data = host.data;
h->value.len = r->host_end - r->host_start;
h->value.data = r->host_start;
h->lowcase_key = host.data;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh == NULL) {
ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
if (hh->handler(r, h, hh->offset) != NGX_OK) {
/*
* request has been finalized already
* in ngx_http_process_host()
*/
return NGX_ERROR;
}
@ -3737,6 +3781,7 @@ ngx_http_v2_construct_cookie_header(ngx_http_request_t *r)
static void
ngx_http_v2_run_request(ngx_http_request_t *r)
{
ngx_str_t host;
ngx_connection_t *fc;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_connection_t *h2c;
@ -3764,12 +3809,54 @@ ngx_http_v2_run_request(ngx_http_request_t *r)
r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
if (ngx_http_process_request_header(r) != NGX_OK) {
if (r->headers_in.server.len == 0) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client sent neither \":authority\" nor \"Host\" header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
goto failed;
}
if (r->host_end) {
host.len = r->host_end - r->host_start;
host.data = r->host_start;
if (r->headers_in.host) {
if (r->headers_in.host->value.len != host.len
|| ngx_memcmp(r->headers_in.host->value.data, host.data,
host.len)
!= 0)
{
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client sent \":authority\" and \"Host\" headers "
"with different values");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
goto failed;
}
} else {
/* compatibility for $http_host */
if (ngx_http_v2_construct_host_header(r) != NGX_OK) {
goto failed;
}
}
}
if (r->headers_in.content_length) {
r->headers_in.content_length_n =
ngx_atoof(r->headers_in.content_length->value.data,
r->headers_in.content_length->value.len);
if (r->headers_in.content_length_n == NGX_ERROR) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client sent invalid \"Content-Length\" header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
goto failed;
}
if (r->headers_in.content_length_n > 0 && r->stream->in_closed) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client prematurely closed stream");
r->stream->skip_data = 1;
@ -3778,10 +3865,22 @@ ngx_http_v2_run_request(ngx_http_request_t *r)
goto failed;
}
if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) {
} else if (!r->stream->in_closed) {
r->headers_in.chunked = 1;
}
if (r->method == NGX_HTTP_CONNECT) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0, "client sent CONNECT method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
goto failed;
}
if (r->method == NGX_HTTP_TRACE) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0, "client sent TRACE method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
goto failed;
}
h2c = r->stream->connection;
h2c->payload_bytes += r->request_length;

View File

@ -213,6 +213,7 @@ struct ngx_http_v2_stream_s {
ngx_pool_t *pool;
unsigned initialized:1;
unsigned waiting:1;
unsigned blocked:1;
unsigned exhausted:1;

View File

@ -27,8 +27,13 @@
#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1
static ngx_int_t ngx_http_v2_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_early_hints_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_init_stream(ngx_http_request_t *r);
static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(
ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin);
ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin,
ngx_uint_t flush);
static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame(
ngx_http_request_t *r);
@ -95,6 +100,7 @@ ngx_module_t ngx_http_v2_filter_module = {
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_header_filter_pt ngx_http_next_early_hints_filter;
static ngx_int_t
@ -107,7 +113,6 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_connection_t *fc;
ngx_http_cleanup_t *cln;
ngx_http_v2_stream_t *stream;
ngx_http_v2_out_frame_t *frame;
ngx_http_v2_connection_t *h2c;
@ -115,10 +120,11 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
ngx_http_core_srv_conf_t *cscf;
u_char addr[NGX_SOCKADDR_STRLEN];
static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7";
static const u_char nginx[5] = { 0x84, 0xaa, 0x63, 0x55, 0xe7 };
#if (NGX_HTTP_GZIP)
static const u_char accept_encoding[12] =
"\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f";
static const u_char accept_encoding[12] = {
0x8b, 0x84, 0x84, 0x2d, 0x69, 0x5b, 0x05, 0x44, 0x3c, 0x86, 0xaa, 0x6f
};
#endif
static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);
@ -604,14 +610,203 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
fin = r->header_only
|| (r->headers_out.content_length_n == 0 && !r->expect_trailers);
frame = ngx_http_v2_create_headers_frame(r, start, pos, fin);
frame = ngx_http_v2_create_headers_frame(r, start, pos, fin, 0);
if (frame == NULL) {
return NGX_ERROR;
}
ngx_http_v2_queue_blocked_frame(h2c, frame);
stream->queued = 1;
stream->queued++;
if (ngx_http_v2_init_stream(r) != NGX_OK) {
return NGX_ERROR;
}
return ngx_http_v2_filter_send(fc, stream);
}
static ngx_int_t
ngx_http_v2_early_hints_filter(ngx_http_request_t *r)
{
u_char *pos, *start, *tmp;
size_t len, tmp_len;
ngx_uint_t i;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_connection_t *fc;
ngx_http_v2_stream_t *stream;
ngx_http_v2_out_frame_t *frame;
ngx_http_v2_connection_t *h2c;
stream = r->stream;
if (!stream) {
return ngx_http_next_early_hints_filter(r);
}
if (r != r->main) {
return NGX_OK;
}
fc = r->connection;
if (fc->error) {
return NGX_ERROR;
}
len = 0;
tmp_len = 0;
part = &r->headers_out.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
"too long response header name: \"%V\"",
&header[i].key);
return NGX_ERROR;
}
if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
"too long response header value: \"%V: %V\"",
&header[i].key, &header[i].value);
return NGX_ERROR;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
+ NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
if (header[i].key.len > tmp_len) {
tmp_len = header[i].key.len;
}
if (header[i].value.len > tmp_len) {
tmp_len = header[i].value.len;
}
}
if (len == 0) {
return NGX_OK;
}
h2c = stream->connection;
len += h2c->table_update ? 1 : 0;
len += 1 + ngx_http_v2_literal_size("418");
tmp = ngx_palloc(r->pool, tmp_len);
pos = ngx_pnalloc(r->pool, len);
if (pos == NULL || tmp == NULL) {
return NGX_ERROR;
}
start = pos;
if (h2c->table_update) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 table size update: 0");
*pos++ = (1 << 5) | 0;
h2c->table_update = 0;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \":status: %03ui\"",
(ngx_uint_t) NGX_HTTP_EARLY_HINTS);
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);
*pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;
pos = ngx_sprintf(pos, "%03ui", (ngx_uint_t) NGX_HTTP_EARLY_HINTS);
part = &r->headers_out.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
#if (NGX_DEBUG)
if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
ngx_strlow(tmp, header[i].key.data, header[i].key.len);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"%*s: %V\"",
header[i].key.len, tmp, &header[i].value);
}
#endif
*pos++ = 0;
pos = ngx_http_v2_write_name(pos, header[i].key.data,
header[i].key.len, tmp);
pos = ngx_http_v2_write_value(pos, header[i].value.data,
header[i].value.len, tmp);
}
frame = ngx_http_v2_create_headers_frame(r, start, pos, 0, 1);
if (frame == NULL) {
return NGX_ERROR;
}
ngx_http_v2_queue_blocked_frame(h2c, frame);
stream->queued++;
if (ngx_http_v2_init_stream(r) != NGX_OK) {
return NGX_ERROR;
}
return ngx_http_v2_filter_send(fc, stream);
}
static ngx_int_t
ngx_http_v2_init_stream(ngx_http_request_t *r)
{
ngx_connection_t *fc;
ngx_http_cleanup_t *cln;
ngx_http_v2_stream_t *stream;
stream = r->stream;
fc = r->connection;
if (stream->initialized) {
return NGX_OK;
}
stream->initialized = 1;
cln = ngx_http_cleanup_add(r, 0);
if (cln == NULL) {
@ -625,13 +820,13 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
fc->need_last_buf = 1;
fc->need_flush_buf = 1;
return ngx_http_v2_filter_send(fc, stream);
return NGX_OK;
}
static ngx_http_v2_out_frame_t *
ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
u_char *end, ngx_uint_t fin)
u_char *end, ngx_uint_t fin, ngx_uint_t flush)
{
u_char type, flags;
size_t rest, frame_size;
@ -722,6 +917,7 @@ ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
}
b->last_buf = fin;
b->flush = flush;
cl->next = NULL;
frame->last = cl;
@ -844,7 +1040,7 @@ ngx_http_v2_create_trailers_frame(ngx_http_request_t *r)
header[i].value.len, tmp);
}
return ngx_http_v2_create_headers_frame(r, start, pos, 1);
return ngx_http_v2_create_headers_frame(r, start, pos, 1, 0);
}
@ -1566,5 +1762,8 @@ ngx_http_v2_filter_init(ngx_conf_t *cf)
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_v2_header_filter;
ngx_http_next_early_hints_filter = ngx_http_top_early_hints_filter;
ngx_http_top_early_hints_filter = ngx_http_v2_early_hints_filter;
return NGX_OK;
}

View File

@ -70,7 +70,7 @@ ngx_http_v3_keepalive_handler(ngx_event_t *ev)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 keepalive handler");
ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR,
ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR,
"keepalive timeout");
}

View File

@ -23,7 +23,7 @@
#define NGX_HTTP_V3_HQ_ALPN_PROTO "\x0Ahq-interop"
#define NGX_HTTP_V3_HQ_PROTO "hq-interop"
#define NGX_HTTP_V3_VARLEN_INT_LEN 4
#define NGX_HTTP_V3_VARLEN_INT_LEN 8
#define NGX_HTTP_V3_PREFIX_INT_LEN 11
#define NGX_HTTP_V3_STREAM_CONTROL 0x00

View File

@ -20,6 +20,7 @@
#define NGX_HTTP_V3_HEADER_METHOD_GET 17
#define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22
#define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23
#define NGX_HTTP_V3_HEADER_STATUS_103 24
#define NGX_HTTP_V3_HEADER_STATUS_200 25
#define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31
#define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53
@ -36,6 +37,7 @@ typedef struct {
static ngx_int_t ngx_http_v3_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_v3_early_hints_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r,
ngx_chain_t *in);
static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r,
@ -75,6 +77,7 @@ ngx_module_t ngx_http_v3_filter_module = {
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_header_filter_pt ngx_http_next_early_hints_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
@ -588,6 +591,150 @@ ngx_http_v3_header_filter(ngx_http_request_t *r)
}
static ngx_int_t
ngx_http_v3_early_hints_filter(ngx_http_request_t *r)
{
size_t len, n;
ngx_buf_t *b;
ngx_uint_t i;
ngx_chain_t *out, *hl, *cl;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_http_v3_session_t *h3c;
if (r->http_version != NGX_HTTP_VERSION_30) {
return ngx_http_next_early_hints_filter(r);
}
if (r != r->main) {
return NGX_OK;
}
len = 0;
part = &r->headers_out.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
len += ngx_http_v3_encode_field_l(NULL, &header[i].key,
&header[i].value);
}
if (len == 0) {
return NGX_OK;
}
len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0);
len += ngx_http_v3_encode_field_ri(NULL, 0, NGX_HTTP_V3_HEADER_STATUS_103);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http3 header len:%uz", len);
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_ERROR;
}
b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last,
0, 0, 0);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http3 output header: \":status: %03ui\"",
(ngx_uint_t) NGX_HTTP_EARLY_HINTS);
b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0,
NGX_HTTP_V3_HEADER_STATUS_103);
part = &r->headers_out.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http3 output header: \"%V: %V\"",
&header[i].key, &header[i].value);
b->last = (u_char *) ngx_http_v3_encode_field_l(b->last,
&header[i].key,
&header[i].value);
}
b->flush = 1;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
cl->next = NULL;
n = b->last - b->pos;
h3c = ngx_http_v3_get_session(r->connection);
h3c->payload_bytes += n;
len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS)
+ ngx_http_v3_encode_varlen_int(NULL, n);
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_ERROR;
}
b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last,
NGX_HTTP_V3_FRAME_HEADERS);
b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n);
hl = ngx_alloc_chain_link(r->pool);
if (hl == NULL) {
return NGX_ERROR;
}
hl->buf = b;
hl->next = cl;
out = hl;
for (cl = out; cl; cl = cl->next) {
h3c->total_bytes += cl->buf->last - cl->buf->pos;
r->header_size += cl->buf->last - cl->buf->pos;
}
return ngx_http_write_filter(r, out);
}
static ngx_int_t
ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
@ -845,6 +992,9 @@ ngx_http_v3_filter_init(ngx_conf_t *cf)
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_v3_header_filter;
ngx_http_next_early_hints_filter = ngx_http_top_early_hints_filter;
ngx_http_top_early_hints_filter = ngx_http_v3_early_hints_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_v3_body_filter;

View File

@ -623,6 +623,12 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st,
}
if (st->huffman) {
if (n > NGX_MAX_INT_T_VALUE / 8) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent too large field line");
return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD;
}
n = n * 8 / 5;
st->huffstate = 0;
}

View File

@ -965,7 +965,7 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r)
if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid host in request line");
"client sent invalid \":authority\" header");
goto failed;
}
@ -1003,6 +1003,7 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r)
{
ssize_t n;
ngx_buf_t *b;
ngx_str_t host;
ngx_connection_t *c;
ngx_http_v3_session_t *h3c;
ngx_http_v3_srv_conf_t *h3scf;
@ -1034,11 +1035,13 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r)
goto failed;
}
if (r->headers_in.host) {
if (r->headers_in.host->value.len != r->headers_in.server.len
|| ngx_memcmp(r->headers_in.host->value.data,
r->headers_in.server.data,
r->headers_in.server.len)
if (r->headers_in.host && r->host_end) {
host.len = r->host_end - r->host_start;
host.data = r->host_start;
if (r->headers_in.host->value.len != host.len
|| ngx_memcmp(r->headers_in.host->value.data, host.data, host.len)
!= 0)
{
ngx_log_error(NGX_LOG_INFO, c->log, 0,
@ -1213,7 +1216,7 @@ ngx_http_v3_construct_cookie_header(ngx_http_request_t *r)
if (hh->handler(r, h, hh->offset) != NGX_OK) {
/*
* request has been finalized already
* in ngx_http_process_multi_header_lines()
* in ngx_http_process_header_line()
*/
return NGX_ERROR;
}

View File

@ -1321,7 +1321,10 @@ ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,
b->last = ngx_copy(b->last, passwd.data, passwd.len);
*b->last++ = CR; *b->last++ = LF;
if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) {
if ((s->auth_method == NGX_MAIL_AUTH_APOP
|| s->auth_method == NGX_MAIL_AUTH_CRAM_MD5)
&& s->salt.len)
{
b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1);
b->last = ngx_copy(b->last, s->salt.data, s->salt.len);

View File

@ -523,7 +523,7 @@ ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)
ngx_int_t
ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
{
u_char *p, *last;
u_char *p, *pos, *last;
ngx_str_t *arg, plain;
arg = s->args.elts;
@ -555,7 +555,7 @@ ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
return NGX_MAIL_PARSE_INVALID_COMMAND;
}
s->login.data = p;
pos = p;
while (p < last && *p) { p++; }
@ -565,7 +565,8 @@ ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
return NGX_MAIL_PARSE_INVALID_COMMAND;
}
s->login.len = p++ - s->login.data;
s->login.len = p++ - pos;
s->login.data = pos;
s->passwd.len = last - p;
s->passwd.data = p;
@ -583,24 +584,26 @@ ngx_int_t
ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,
ngx_uint_t n)
{
ngx_str_t *arg;
ngx_str_t *arg, login;
arg = s->args.elts;
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"mail auth login username: \"%V\"", &arg[n]);
s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
if (s->login.data == NULL) {
login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
if (login.data == NULL) {
return NGX_ERROR;
}
if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) {
if (ngx_decode_base64(&login, &arg[n]) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid base64 encoding in AUTH LOGIN command");
return NGX_MAIL_PARSE_INVALID_COMMAND;
}
s->login = login;
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"mail auth login username: \"%V\"", &s->login);
@ -611,7 +614,7 @@ ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,
ngx_int_t
ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
{
ngx_str_t *arg;
ngx_str_t *arg, passwd;
arg = s->args.elts;
@ -620,18 +623,19 @@ ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
"mail auth login password: \"%V\"", &arg[0]);
#endif
s->passwd.data = ngx_pnalloc(c->pool,
ngx_base64_decoded_length(arg[0].len));
if (s->passwd.data == NULL) {
passwd.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
if (passwd.data == NULL) {
return NGX_ERROR;
}
if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {
if (ngx_decode_base64(&passwd, &arg[0]) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid base64 encoding in AUTH LOGIN command");
return NGX_MAIL_PARSE_INVALID_COMMAND;
}
s->passwd = passwd;
#if (NGX_DEBUG_MAIL_PASSWD)
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"mail auth login password: \"%V\"", &s->passwd);
@ -674,24 +678,26 @@ ngx_int_t
ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
{
u_char *p, *last;
ngx_str_t *arg;
ngx_str_t *arg, login;
arg = s->args.elts;
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"mail auth cram-md5: \"%V\"", &arg[0]);
s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
if (s->login.data == NULL) {
login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
if (login.data == NULL) {
return NGX_ERROR;
}
if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
if (ngx_decode_base64(&login, &arg[0]) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid base64 encoding in AUTH CRAM-MD5 command");
return NGX_MAIL_PARSE_INVALID_COMMAND;
}
s->login = login;
p = s->login.data;
last = p + s->login.len;
@ -1000,13 +1006,11 @@ ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)
len -= p - buf;
buf = p;
if (s->login.len == 0) {
return p;
}
if (s->login.len) {
p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);
len -= p - buf;
buf = p;
}
if (s->proxy == NULL) {
return p;

View File

@ -531,6 +531,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
ngx_int_t rc;
ngx_str_t line, auth, encoded;
ngx_buf_t *b;
uintptr_t n;
ngx_connection_t *c;
ngx_mail_session_t *s;
ngx_mail_proxy_conf_t *pcf;
@ -627,6 +628,10 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
CRLF) - 1
+ s->connection->addr_text.len + s->login.len + s->host.len;
n = ngx_escape_uri(NULL, s->login.data, s->login.len,
NGX_ESCAPE_MAIL_XTEXT);
line.len += n * 2;
#if (NGX_HAVE_INET6)
if (s->connection->sockaddr->sa_family == AF_INET6) {
line.len += sizeof("IPV6:") - 1;
@ -654,7 +659,14 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
if (s->login.len && !pcf->smtp_auth) {
p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
if (n == 0) {
p = ngx_copy(p, s->login.data, s->login.len);
} else {
p = (u_char *) ngx_escape_uri(p, s->login.data, s->login.len,
NGX_ESCAPE_MAIL_XTEXT);
}
}
p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);

View File

@ -782,6 +782,9 @@ ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)
ngx_str_set(&s->out, smtp_ok);
ngx_str_null(&s->login);
ngx_str_null(&s->passwd);
return NGX_OK;
}

View File

@ -97,6 +97,13 @@ static ngx_command_t ngx_mail_ssl_commands[] = {
0,
NULL },
{ ngx_string("ssl_certificate_compression"),
NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_MAIL_SRV_CONF_OFFSET,
offsetof(ngx_mail_ssl_conf_t, certificate_compression),
NULL },
{ ngx_string("ssl_dhparam"),
NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
@ -314,6 +321,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
scf->passwords = NGX_CONF_UNSET_PTR;
scf->conf_commands = NGX_CONF_UNSET_PTR;
scf->prefer_server_ciphers = NGX_CONF_UNSET;
scf->certificate_compression = NGX_CONF_UNSET;
scf->verify = NGX_CONF_UNSET_UINT;
scf->verify_depth = NGX_CONF_UNSET_UINT;
scf->builtin_session_cache = NGX_CONF_UNSET;
@ -343,6 +351,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->prefer_server_ciphers,
prev->prefer_server_ciphers, 0);
ngx_conf_merge_value(conf->certificate_compression,
prev->certificate_compression, 0);
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
(NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS));
@ -446,6 +457,13 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
}
if (ngx_ssl_certificate_compression(cf, &conf->ssl,
conf->certificate_compression)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
if (conf->verify) {
if (conf->verify != 3

View File

@ -21,6 +21,7 @@
typedef struct {
ngx_flag_t prefer_server_ciphers;
ngx_flag_t certificate_compression;
ngx_ssl_t ssl;

View File

@ -52,7 +52,6 @@
#include <malloc.h> /* memalign() */
#include <limits.h> /* IOV_MAX */
#include <sys/ioctl.h>
#include <crypt.h>
#include <sys/utsname.h> /* uname() */
#include <dlfcn.h>
@ -61,6 +60,11 @@
#include <ngx_auto_config.h>
#if (NGX_HAVE_CRYPT_H)
#include <crypt.h>
#endif
#if (NGX_HAVE_POSIX_SEM)
#include <semaphore.h>
#endif

View File

@ -43,7 +43,7 @@ ngx_timezone_update(void)
struct tm *t;
char buf[4];
s = time(0);
s = time(NULL);
t = localtime(&s);

View File

@ -41,7 +41,7 @@ ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
return NGX_ERROR;
}
#else
#elif (NGX_HAVE_CRYPT)
ngx_int_t
ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
@ -71,6 +71,14 @@ ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
return NGX_ERROR;
}
#else
ngx_int_t
ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
{
return NGX_ERROR;
}
#endif
#endif /* NGX_CRYPT */

View File

@ -49,6 +49,7 @@ typedef struct {
ngx_str_t ssl_crl;
ngx_stream_complex_value_t *ssl_certificate;
ngx_stream_complex_value_t *ssl_certificate_key;
ngx_ssl_cache_t *ssl_certificate_cache;
ngx_array_t *ssl_passwords;
ngx_array_t *ssl_conf_commands;
@ -94,6 +95,8 @@ static char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd,
#if (NGX_STREAM_SSL)
static ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s);
static char *ngx_stream_proxy_ssl_certificate_cache(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_stream_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post,
@ -105,6 +108,8 @@ static ngx_int_t ngx_stream_proxy_ssl_name(ngx_stream_session_t *s);
static ngx_int_t ngx_stream_proxy_ssl_certificate(ngx_stream_session_t *s);
static ngx_int_t ngx_stream_proxy_merge_ssl(ngx_conf_t *cf,
ngx_stream_proxy_srv_conf_t *conf, ngx_stream_proxy_srv_conf_t *prev);
static ngx_int_t ngx_stream_proxy_merge_ssl_passwords(ngx_conf_t *cf,
ngx_stream_proxy_srv_conf_t *conf, ngx_stream_proxy_srv_conf_t *prev);
static ngx_int_t ngx_stream_proxy_set_ssl(ngx_conf_t *cf,
ngx_stream_proxy_srv_conf_t *pscf);
@ -341,6 +346,13 @@ static ngx_command_t ngx_stream_proxy_commands[] = {
offsetof(ngx_stream_proxy_srv_conf_t, ssl_certificate_key),
NULL },
{ ngx_string("proxy_ssl_certificate_cache"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE123,
ngx_stream_proxy_ssl_certificate_cache,
NGX_STREAM_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("proxy_ssl_password_file"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_stream_proxy_ssl_password_file,
@ -1029,6 +1041,100 @@ ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s)
}
static char *
ngx_stream_proxy_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_stream_proxy_srv_conf_t *pscf = conf;
time_t inactive, valid;
ngx_str_t *value, s;
ngx_int_t max;
ngx_uint_t i;
if (pscf->ssl_certificate_cache != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
max = 0;
inactive = 10;
valid = 60;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
if (max <= 0) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
s.len = value[i].len - 9;
s.data = value[i].data + 9;
inactive = ngx_parse_time(&s, 1);
if (inactive == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
s.len = value[i].len - 6;
s.data = value[i].data + 6;
valid = ngx_parse_time(&s, 1);
if (valid == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strcmp(value[i].data, "off") == 0) {
pscf->ssl_certificate_cache = NULL;
continue;
}
failed:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (pscf->ssl_certificate_cache == NULL) {
return NGX_CONF_OK;
}
if (max == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_ssl_certificate_cache\" must have "
"the \"max\" parameter");
return NGX_CONF_ERROR;
}
pscf->ssl_certificate_cache = ngx_ssl_cache_init(cf->pool, max, valid,
inactive);
if (pscf->ssl_certificate_cache == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
@ -1325,6 +1431,7 @@ ngx_stream_proxy_ssl_certificate(ngx_stream_session_t *s)
"stream upstream ssl key: \"%s\"", key.data);
if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key,
pscf->ssl_certificate_cache,
pscf->ssl_passwords)
!= NGX_OK)
{
@ -2120,6 +2227,7 @@ ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf)
conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
conf->ssl_certificate = NGX_CONF_UNSET_PTR;
conf->ssl_certificate_key = NGX_CONF_UNSET_PTR;
conf->ssl_certificate_cache = NGX_CONF_UNSET_PTR;
conf->ssl_passwords = NGX_CONF_UNSET_PTR;
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
#endif
@ -2206,7 +2314,12 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_ptr_value(conf->ssl_certificate_key,
prev->ssl_certificate_key, NULL);
ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
ngx_conf_merge_ptr_value(conf->ssl_certificate_cache,
prev->ssl_certificate_cache, NULL);
if (ngx_stream_proxy_merge_ssl_passwords(cf, conf, prev) != NGX_OK) {
return NGX_CONF_ERROR;
}
ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
prev->ssl_conf_commands, NULL);
@ -2272,6 +2385,57 @@ ngx_stream_proxy_merge_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *conf,
}
static ngx_int_t
ngx_stream_proxy_merge_ssl_passwords(ngx_conf_t *cf,
ngx_stream_proxy_srv_conf_t *conf, ngx_stream_proxy_srv_conf_t *prev)
{
ngx_uint_t preserve;
ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
if (conf->ssl_certificate == NULL
|| conf->ssl_certificate->value.len == 0
|| conf->ssl_certificate_key == NULL)
{
return NGX_OK;
}
if (conf->ssl_certificate->lengths == NULL
&& conf->ssl_certificate_key->lengths == NULL)
{
if (conf->ssl_passwords && conf->ssl_passwords->pool == NULL) {
/* un-preserve empty password list */
conf->ssl_passwords = NULL;
}
return NGX_OK;
}
if (conf->ssl_passwords && conf->ssl_passwords->pool != cf->temp_pool) {
/* already preserved */
return NGX_OK;
}
preserve = (conf->ssl_passwords == prev->ssl_passwords) ? 1 : 0;
conf->ssl_passwords = ngx_ssl_preserve_passwords(cf, conf->ssl_passwords);
if (conf->ssl_passwords == NULL) {
return NGX_ERROR;
}
/*
* special handling to keep a preserved ssl_passwords copy
* in the previous configuration to inherit it to all children
*/
if (preserve) {
prev->ssl_passwords = conf->ssl_passwords;
}
return NGX_OK;
}
static ngx_int_t
ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf)
{
@ -2309,16 +2473,9 @@ ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf)
return NGX_ERROR;
}
if (pscf->ssl_certificate->lengths
|| pscf->ssl_certificate_key->lengths)
if (pscf->ssl_certificate->lengths == NULL
&& pscf->ssl_certificate_key->lengths == NULL)
{
pscf->ssl_passwords =
ngx_ssl_preserve_passwords(cf, pscf->ssl_passwords);
if (pscf->ssl_passwords == NULL) {
return NGX_ERROR;
}
} else {
if (ngx_ssl_certificate(cf, pscf->ssl,
&pscf->ssl_certificate->value,
&pscf->ssl_certificate_key->value,

View File

@ -47,6 +47,8 @@ static char *ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent,
static ngx_int_t ngx_stream_ssl_compile_certificates(ngx_conf_t *cf,
ngx_stream_ssl_srv_conf_t *conf);
static char *ngx_stream_ssl_certificate_cache(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
@ -117,6 +119,13 @@ static ngx_command_t ngx_stream_ssl_commands[] = {
offsetof(ngx_stream_ssl_srv_conf_t, certificate_keys),
NULL },
{ ngx_string("ssl_certificate_cache"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE123,
ngx_stream_ssl_certificate_cache,
NGX_STREAM_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("ssl_password_file"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_stream_ssl_password_file,
@ -124,6 +133,13 @@ static ngx_command_t ngx_stream_ssl_commands[] = {
0,
NULL },
{ ngx_string("ssl_certificate_compression"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_stream_ssl_srv_conf_t, certificate_compression),
NULL },
{ ngx_string("ssl_dhparam"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
@ -539,8 +555,27 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
if (c->ssl->sni_accepted) {
return SSL_TLSEXT_ERR_OK;
}
if (c->ssl->handshake_rejected) {
*ad = SSL_AD_UNRECOGNIZED_NAME;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
s = c->data;
if (arg) {
host = *(ngx_str_t *) arg;
if (host.data == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
"SSL server name: null");
goto done;
}
} else {
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
if (servername == NULL) {
@ -549,17 +584,17 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
goto done;
}
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
"SSL server name: \"%s\"", servername);
host.len = ngx_strlen(servername);
host.data = (u_char *) servername;
}
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
"SSL server name: \"%V\"", &host);
if (host.len == 0) {
goto done;
}
host.data = (u_char *) servername;
rc = ngx_stream_validate_host(&host, c->pool, 1);
if (rc == NGX_ERROR) {
@ -584,7 +619,7 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
ngx_set_connection_log(c, cscf->error_log);
sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
sscf = ngx_stream_get_module_srv_conf(cscf->ctx, ngx_stream_ssl_module);
if (sscf->ssl.ctx) {
if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) {
@ -624,6 +659,7 @@ done:
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
c->ssl->sni_accepted = 1;
return SSL_TLSEXT_ERR_OK;
error:
@ -718,6 +754,7 @@ ngx_stream_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg)
"ssl key: \"%s\"", key.data);
if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key,
sscf->certificate_cache,
sscf->passwords)
!= NGX_OK)
{
@ -844,9 +881,11 @@ ngx_stream_ssl_create_srv_conf(ngx_conf_t *cf)
sscf->handshake_timeout = NGX_CONF_UNSET_MSEC;
sscf->certificates = NGX_CONF_UNSET_PTR;
sscf->certificate_keys = NGX_CONF_UNSET_PTR;
sscf->certificate_cache = NGX_CONF_UNSET_PTR;
sscf->passwords = NGX_CONF_UNSET_PTR;
sscf->conf_commands = NGX_CONF_UNSET_PTR;
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
sscf->certificate_compression = NGX_CONF_UNSET;
sscf->reject_handshake = NGX_CONF_UNSET;
sscf->verify = NGX_CONF_UNSET_UINT;
sscf->verify_depth = NGX_CONF_UNSET_UINT;
@ -880,6 +919,9 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->prefer_server_ciphers,
prev->prefer_server_ciphers, 0);
ngx_conf_merge_value(conf->certificate_compression,
prev->certificate_compression, 0);
ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0);
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
@ -892,6 +934,9 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
NULL);
ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache,
NULL);
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
@ -954,8 +999,14 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
cln->data = &conf->ssl;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
{
static ngx_ssl_client_hello_arg cb = { ngx_stream_ssl_servername };
ngx_ssl_set_client_hello_callback(conf->ssl.ctx, &cb);
SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
ngx_stream_ssl_servername);
}
#endif
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
@ -1002,6 +1053,13 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
return NGX_CONF_ERROR;
}
if (ngx_ssl_certificate_compression(cf, &conf->ssl,
conf->certificate_compression)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
if (conf->verify) {
@ -1097,13 +1155,19 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->stapling) {
if (conf->certificate_compression) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"\"ssl_stapling\" is incompatible with "
"\"ssl_certificate_compression\"");
return NGX_CONF_ERROR;
}
if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
&conf->stapling_responder, conf->stapling_verify)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) {
@ -1202,6 +1266,99 @@ found:
}
static char *
ngx_stream_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_stream_ssl_srv_conf_t *sscf = conf;
time_t inactive, valid;
ngx_str_t *value, s;
ngx_int_t max;
ngx_uint_t i;
if (sscf->certificate_cache != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
max = 0;
inactive = 10;
valid = 60;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
if (max <= 0) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
s.len = value[i].len - 9;
s.data = value[i].data + 9;
inactive = ngx_parse_time(&s, 1);
if (inactive == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
s.len = value[i].len - 6;
s.data = value[i].data + 6;
valid = ngx_parse_time(&s, 1);
if (valid == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strcmp(value[i].data, "off") == 0) {
sscf->certificate_cache = NULL;
continue;
}
failed:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (sscf->certificate_cache == NULL) {
return NGX_CONF_OK;
}
if (max == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"ssl_certificate_cache\" must have "
"the \"max\" parameter");
return NGX_CONF_ERROR;
}
sscf->certificate_cache = ngx_ssl_cache_init(cf->pool, max, valid,
inactive);
if (sscf->certificate_cache == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{

View File

@ -18,6 +18,7 @@ typedef struct {
ngx_msec_t handshake_timeout;
ngx_flag_t prefer_server_ciphers;
ngx_flag_t certificate_compression;
ngx_flag_t reject_handshake;
ngx_ssl_t ssl;
@ -37,6 +38,8 @@ typedef struct {
ngx_array_t *certificate_values;
ngx_array_t *certificate_key_values;
ngx_ssl_cache_t *certificate_cache;
ngx_str_t dhparam;
ngx_str_t ecdh_curve;
ngx_str_t client_certificate;

View File

@ -911,7 +911,6 @@ ngx_stream_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
int len;
const u_char *p;
ngx_stream_upstream_rr_peers_t *peers;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
#endif
peer = rrp->current;
@ -931,12 +930,12 @@ ngx_stream_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
len = peer->ssl_session_len;
ngx_memcpy(buf, peer->ssl_session, len);
ngx_memcpy(ngx_ssl_session_buffer, peer->ssl_session, len);
ngx_stream_upstream_rr_peer_unlock(peers, peer);
ngx_stream_upstream_rr_peers_unlock(peers);
p = buf;
p = ngx_ssl_session_buffer;
ssl_session = d2i_SSL_SESSION(NULL, &p, len);
rc = ngx_ssl_set_session(pc->connection, ssl_session);
@ -973,7 +972,6 @@ ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
int len;
u_char *p;
ngx_stream_upstream_rr_peers_t *peers;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
#endif
#if (NGX_STREAM_UPSTREAM_ZONE)
@ -987,18 +985,18 @@ ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
return;
}
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
"save session: %p", ssl_session);
len = i2d_SSL_SESSION(ssl_session, NULL);
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
"save session: %p:%d", ssl_session, len);
/* do not cache too big session */
if (len > NGX_SSL_MAX_SESSION_SIZE) {
return;
}
p = buf;
p = ngx_ssl_session_buffer;
(void) i2d_SSL_SESSION(ssl_session, &p);
peer = rrp->current;
@ -1028,7 +1026,7 @@ ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
peer->ssl_session_len = len;
}
ngx_memcpy(peer->ssl_session, buf, len);
ngx_memcpy(peer->ssl_session, ngx_ssl_session_buffer, len);
ngx_stream_upstream_rr_peer_unlock(peers, peer);
ngx_stream_upstream_rr_peers_unlock(peers);
@ -1056,8 +1054,6 @@ ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
"old session: %p", old_ssl_session);
/* TODO: may block */
ngx_ssl_free_session(old_ssl_session);
}
}