Compare commits

...

47 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
f1beff1e10 src: Enable TLS 1.3 with boringssl
This commit enables TLS 1.3 using latest boringssl.  This doesn't
compile with OpenSSL or libressl at the moment.
2017-01-09 23:16:15 +09:00
Tatsuhiro Tsujikawa
b1b8308555 Update doc 2017-01-09 22:22:04 +09:00
Tatsuhiro Tsujikawa
9b574a5a76 nghttpx: Fix typo 2017-01-09 22:19:19 +09:00
Tatsuhiro Tsujikawa
0567f1f038 Add constexpr to StringRef(const CharT *, size_t) 2017-01-09 21:15:53 +09:00
Tatsuhiro Tsujikawa
4be5de1163 src: Move log related functions from util.cc to shrpx_log.cc 2017-01-09 19:34:40 +09:00
Tatsuhiro Tsujikawa
9db1c9467c src: Add constexpr to long_options 2017-01-09 19:28:00 +09:00
Tatsuhiro Tsujikawa
3444b42d44 src: Add more constexpr 2017-01-09 17:17:48 +09:00
Tatsuhiro Tsujikawa
6595ae26ea src: Add constexpr to const objects 2017-01-09 17:11:37 +09:00
Tatsuhiro Tsujikawa
41d8a3ac09 Document PROX protocol and PSK in nghttpx howto 2017-01-09 16:33:51 +09:00
Tatsuhiro Tsujikawa
175001a8d9 Add migration section from nghttpx v1.18.x 2017-01-09 16:09:30 +09:00
Tatsuhiro Tsujikawa
7cf9e00283 Update bash_completion 2017-01-09 14:56:03 +09:00
Tatsuhiro Tsujikawa
8a3eb3f066 Update man pages 2017-01-09 14:55:51 +09:00
Tatsuhiro Tsujikawa
7e1a0d204b h2load: Show default cipher list in -h 2017-01-09 14:47:32 +09:00
Tatsuhiro Tsujikawa
cbca2e35b5 nghttpx: Show default cipher list in -h 2017-01-09 14:43:13 +09:00
Tatsuhiro Tsujikawa
fc9bdf024f src: Make DEFAULT_CIPHER_LIST constexpr char[] 2017-01-09 14:42:40 +09:00
Tatsuhiro Tsujikawa
3f97e6cd3a Merge pull request #776 from nghttp2/nghttpx-memchunkbuffer
nghttpx: Use Memchunk based read buffer for frontend connection
2017-01-09 13:08:23 +09:00
Tatsuhiro Tsujikawa
4fa150c494 nghttpx: Use Memchunk based read buffer for frontend connection
Previously, we have dedicated read buffer for each frontend
connection.  With this commit, the buffer spaces are only used when
needed, and pooled if they are not used.  This reduces memory usage
for idle client connections.
2017-01-08 23:20:14 +09:00
Tatsuhiro Tsujikawa
e8b2508036 nghttpx: Rename confusing names in HttpDownstreamConnection 2017-01-08 23:09:00 +09:00
Tatsuhiro Tsujikawa
ac399e41ac nghttpx: Update doc
Mention client-ciphers, and no-http2-cipher-black-list options in
psk-secrets and client-psk-secrets options.
2017-01-08 23:04:07 +09:00
Tatsuhiro Tsujikawa
95dd908834 Merge branch 'nghttpx-more-tls-options' 2017-01-08 22:57:21 +09:00
Tatsuhiro Tsujikawa
9c7e54d9b5 nghttpx: Add client-ciphers option
Previously, ciphers option sets cipher list for both frontend and
backend TLS connections.  With this commit, ciphers option only sets
cipher list for frontend connections.  The new client-ciphers option
sets cipher list for backend connection.
2017-01-08 22:40:58 +09:00
Tatsuhiro Tsujikawa
3c03024881 nghttpx: Add client-no-http2-cipher-black-list option
This commit adds client-no-http2-cipher-black-list option to disable
enforcement of HTTP/2 cipher black list on backend HTTP/2 connection.
Previously, existing no-http2-cipher-black-list option disables it for
both frontend and backend connections.  Now no-http2-cipher-black-list
option only disables it for frontend connection.
2017-01-08 22:33:19 +09:00
Tatsuhiro Tsujikawa
36dfc0a56a nghttpx: Reorganize client side TLS configuration 2017-01-08 22:25:30 +09:00
Tatsuhiro Tsujikawa
55bf6cdb15 Merge branch 'nghttpx-psk' 2017-01-08 21:10:07 +09:00
Tatsuhiro Tsujikawa
0abc220013 nghttpx: Fix the bug that no-http2-cipher-black-list does not work
Because of the redundant check in backend HTTP/2 session,
no-http2-cipher-black-list does not work on backend HTTP/2 connection.
This commit fixes it.
2017-01-08 19:43:24 +09:00
Tatsuhiro Tsujikawa
c28900990a h2load: Show custom server temp key such as X25519 2017-01-08 17:58:19 +09:00
Tatsuhiro Tsujikawa
5108193d7b h2load: Fix incorrect return value from spdylay_send_callback 2017-01-08 17:32:35 +09:00
Tatsuhiro Tsujikawa
79a24f5dd9 nghttpx: Add --client-psk-secret option to enable PSK in backend 2017-01-08 00:35:55 +09:00
Tatsuhiro Tsujikawa
83c759572c nghttpx: Add --psk-secret option to enable PSK in frontend connection 2017-01-08 00:35:54 +09:00
Tatsuhiro Tsujikawa
1a07fb000b nghttpx: Enable SCT with OpenSSL 1.1.0 2017-01-06 21:29:04 +09:00
Tatsuhiro Tsujikawa
4aab15999d Merge pull request #769 from alagoutte/pvs
Fix issue reporting by PVS Studio
2017-01-04 20:27:55 +09:00
Tatsuhiro Tsujikawa
441982674f Merge pull request #768 from makovich/master
Update README file
2017-01-04 20:24:04 +09:00
Alexis La Goutte
8256c6e070 libevent-client: fix Incorrect format found by PVS Studio (V576)
Consider checking the fourth actual argument of the 'fprintf' function. The SIGNED integer type argument is expected.
2017-01-03 22:03:53 +01:00
Alexis La Goutte
ae87a44b94 nghttp2_hd: fix It is odd that the body of 'hd_get_num_table_entries' function is fully equivalent to the body of 'get_max_index' function found by PVS Studio (V524) 2017-01-03 22:03:53 +01:00
Alexis La Goutte
87d1692e27 nghttp2_submit: fix Parameter 'flags' is always rewritten in function body before being used found by PVS Studio (V763) 2017-01-03 22:03:53 +01:00
makovich
1d2f008656 Update README file 2017-01-03 23:00:32 +03:00
Tatsuhiro Tsujikawa
b064d8a9ff Merge branch 'nghttpx-fronend-proxyproto' 2017-01-03 17:28:20 +09:00
Tatsuhiro Tsujikawa
528af200b6 Merge branch 'nghttpx-fix-libev-assertion-error' 2017-01-03 17:26:05 +09:00
Tatsuhiro Tsujikawa
c6827a7dac nghttpx: Fix assertion error in libev ev_io_start 2017-01-03 16:43:49 +09:00
Tatsuhiro Tsujikawa
55ecb082ee nghttpx: Handle c-ares success without result 2017-01-03 14:35:05 +09:00
Tatsuhiro Tsujikawa
5f2cf461e6 integration: Avoid nghttpx accept-proxy-protocol option 2017-01-03 14:14:37 +09:00
Tatsuhiro Tsujikawa
b313386988 nghttpx: Add proxyproto to frontend option to accept PROXY protocol
Previously, global accept-proxy-protocol option enables PROXY protocol
support for all frontend listeners, but this was inflexible.  To fix
this issue, accept-proxy-protocol option is now deprecated, and
instead proxyproto parameter in frontend option enables PROXY protocol
support per frontend.
2017-01-03 12:47:03 +09:00
Tatsuhiro Tsujikawa
3933280d29 src: Fix assertion error with boringssl
boringssl says:

/* It is an error to clear any bits that have already been set. (We can't try
 * to get a second close_notify or send two.) */
assert((SSL_get_shutdown(ssl) & mode) == SSL_get_shutdown(ssl));
2017-01-02 11:48:38 +09:00
Tatsuhiro Tsujikawa
2b6073900f Merge branch 'nghttpx-dns-timeout-fix' 2016-12-30 11:39:28 +09:00
Tatsuhiro Tsujikawa
d1ba43a69f nghttpx: Fix bug that DNS timeout was erroneously disabled 2016-12-30 11:09:02 +09:00
Tatsuhiro Tsujikawa
a0779edec4 nghttpx: Fix bug that DNS timeout was ignored 2016-12-30 11:08:26 +09:00
Tatsuhiro Tsujikawa
d70fefe72f Bump up version number to 1.19.0-DEV 2016-12-27 20:55:00 +09:00
51 changed files with 977 additions and 298 deletions

View File

@@ -24,7 +24,7 @@
cmake_minimum_required(VERSION 3.0)
# XXX using 1.8.90 instead of 1.9.0-DEV
project(nghttp2 VERSION 1.18.0)
project(nghttp2 VERSION 1.18.90)
# See versioning rule:
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html

View File

@@ -99,6 +99,11 @@ To mitigate heap fragmentation in long running server programs
* jemalloc
.. note::
Alpine Linux currently does not support malloc replacement
due to musl limitations. See details in issue `#762 <https://github.com/nghttp2/nghttp2/issues/762>`_.
libnghttp2_asio C++ library requires the following packages:
* libboost-dev >= 1.54.0

View File

@@ -25,7 +25,7 @@ dnl Do not change user variables!
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.18.0], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [1.19.0-DEV], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])

View File

@@ -8,7 +8,7 @@ _nghttpx()
_get_comp_words_by_ref cur prev
case $cur in
-*)
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --fastopen --tls-ticket-key-memcached --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --stream-read-timeout --backend-connect-timeout --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --rlimit-nofile --tls-ticket-key-memcached-cert-file --ocsp-update-interval --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-proto-list --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --no-server-push --no-location-rewrite --tls-session-cache-memcached --no-ocsp --frontend-http2-encoder-dynamic-table-size --workers --add-forwarded --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --add-x-forwarded-for --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --client-cert-file --accept-proxy-protocol --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --backend-response-buffer --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --fastopen --backend-connect-timeout --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --rlimit-nofile --tls-ticket-key-memcached-cert-file --ocsp-update-interval --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-proto-list --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --no-server-push --no-location-rewrite --tls-session-cache-memcached --no-ocsp --frontend-http2-encoder-dynamic-table-size --workers --add-forwarded --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --add-x-forwarded-for --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --backend-response-buffer --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
;;
*)
_filedir

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "H2LOAD" "1" "Dec 27, 2016" "1.18.0" "nghttp2"
.TH "H2LOAD" "1" "Jan 09, 2017" "1.19.0-DEV" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.
@@ -123,6 +123,8 @@ Add/Override a header to the requests.
.B \-\-ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
.sp
Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
.UNINDENT
.INDENT 0.0
.TP

View File

@@ -96,6 +96,8 @@ OPTIONS
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
.. option:: -p, --no-tls-proto=<PROTOID>
Specify ALPN identifier of the protocol to be used when

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTP" "1" "Dec 27, 2016" "1.18.0" "nghttp2"
.TH "NGHTTP" "1" "Jan 09, 2017" "1.19.0-DEV" "nghttp2"
.SH NAME
nghttp \- HTTP/2 client
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPD" "1" "Dec 27, 2016" "1.18.0" "nghttp2"
.TH "NGHTTPD" "1" "Jan 09, 2017" "1.19.0-DEV" "nghttp2"
.SH NAME
nghttpd \- HTTP/2 server
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPX" "1" "Dec 27, 2016" "1.18.0" "nghttp2"
.TH "NGHTTPX" "1" "Jan 09, 2017" "1.19.0-DEV" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 proxy
.
@@ -218,6 +218,10 @@ specify "healthmon" parameter. This is disabled by
default. Any requests which come through this address
are replied with 200 HTTP status, without no body.
.sp
To accept PROXY protocol version 1 on frontend
connection, specify "proxyproto" parameter. This is
disabled by default.
.sp
Default: \fB*,3000\fP
.UNINDENT
.INDENT 0.0
@@ -253,11 +257,6 @@ timeouts when connecting and making CONNECT request can
be specified by \fI\%\-\-backend\-read\-timeout\fP and
\fI\%\-\-backend\-write\-timeout\fP options.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-accept\-proxy\-protocol
Accept PROXY protocol version 1 on frontend connection.
.UNINDENT
.SS Performance
.INDENT 0.0
.TP
@@ -529,8 +528,18 @@ Default: \fB2m\fP
.INDENT 0.0
.TP
.B \-\-ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
.sp
Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-client\-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
.sp
Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -820,9 +829,18 @@ Default: \fB1s\fP
.INDENT 0.0
.TP
.B \-\-no\-http2\-cipher\-black\-list
Allow black listed cipher suite on HTTP/2 connection.
See \fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for
the complete HTTP/2 cipher suites black list.
Allow black listed cipher suite on frontend HTTP/2
connection. See
\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for the
complete HTTP/2 cipher suites black list.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-client\-no\-http2\-cipher\-black\-list
Allow black listed cipher suite on backend HTTP/2
connection. See
\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for the
complete HTTP/2 cipher suites black list.
.UNINDENT
.INDENT 0.0
.TP
@@ -836,6 +854,39 @@ argument <CERT>, or certificate option in configuration
file. For additional certificates, use \fI\%\-\-subcert\fP
option. This option requires OpenSSL >= 1.0.2.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-psk\-secrets=<PATH>
Read list of PSK identity and secrets from <PATH>. This
is used for frontend connection. The each line of input
file is formatted as <identity>:<hex\-secret>, where
<identity> is PSK identity, and <hex\-secret> is secret
in hex. An empty line, and line which starts with \(aq#\(aq
are skipped. The default enabled cipher list might not
contain any PSK cipher suite. In that case, desired PSK
cipher suites must be enabled using \fI\%\-\-ciphers\fP option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use \fI\%\-\-no\-http2\-cipher\-black\-list\fP option.
But be aware its implications.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-client\-psk\-secrets=<PATH>
Read PSK identity and secrets from <PATH>. This is used
for backend connection. The each line of input file is
formatted as <identity>:<hex\-secret>, where <identity>
is PSK identity, and <hex\-secret> is secret in hex. An
empty line, and line which starts with \(aq#\(aq are skipped.
The first identity and secret pair encountered is used.
The default enabled cipher list might not contain any
PSK cipher suite. In that case, desired PSK cipher
suites must be enabled using \fI\%\-\-client\-ciphers\fP option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use \fI\%\-\-client\-no\-http2\-cipher\-black\-list\fP
option. But be aware its implications.
.UNINDENT
.SS HTTP/2 and SPDY
.INDENT 0.0
.TP
@@ -1255,7 +1306,7 @@ backend server, the custom error pages are not used.
.B \-\-server\-name=<NAME>
Change server response header field value to <NAME>.
.sp
Default: \fBnghttpx nghttp2/1.18.0\fP
Default: \fBnghttpx nghttp2/1.19.0\-DEV\fP
.UNINDENT
.INDENT 0.0
.TP

View File

@@ -202,6 +202,10 @@ Connections
default. Any requests which come through this address
are replied with 200 HTTP status, without no body.
To accept PROXY protocol version 1 on frontend
connection, specify "proxyproto" parameter. This is
disabled by default.
Default: ``*,3000``
@@ -235,10 +239,6 @@ Connections
be specified by :option:`--backend-read-timeout` and
:option:`--backend-write-timeout` options.
.. option:: --accept-proxy-protocol
Accept PROXY protocol version 1 on frontend connection.
Performance
~~~~~~~~~~~
@@ -487,8 +487,17 @@ SSL/TLS
.. option:: --ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
.. option:: --client-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
.. option:: --ecdh-curves=<LIST>
@@ -747,9 +756,17 @@ SSL/TLS
.. option:: --no-http2-cipher-black-list
Allow black listed cipher suite on HTTP/2 connection.
See https://tools.ietf.org/html/rfc7540#appendix-A for
the complete HTTP/2 cipher suites black list.
Allow black listed cipher suite on frontend HTTP/2
connection. See
https://tools.ietf.org/html/rfc7540#appendix-A for the
complete HTTP/2 cipher suites black list.
.. option:: --client-no-http2-cipher-black-list
Allow black listed cipher suite on backend HTTP/2
connection. See
https://tools.ietf.org/html/rfc7540#appendix-A for the
complete HTTP/2 cipher suites black list.
.. option:: --tls-sct-dir=<DIR>
@@ -762,6 +779,37 @@ SSL/TLS
file. For additional certificates, use :option:`--subcert`
option. This option requires OpenSSL >= 1.0.2.
.. option:: --psk-secrets=<PATH>
Read list of PSK identity and secrets from <PATH>. This
is used for frontend connection. The each line of input
file is formatted as <identity>:<hex-secret>, where
<identity> is PSK identity, and <hex-secret> is secret
in hex. An empty line, and line which starts with '#'
are skipped. The default enabled cipher list might not
contain any PSK cipher suite. In that case, desired PSK
cipher suites must be enabled using :option:`--ciphers` option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use :option:`--no-http2-cipher-black-list` option.
But be aware its implications.
.. option:: --client-psk-secrets=<PATH>
Read PSK identity and secrets from <PATH>. This is used
for backend connection. The each line of input file is
formatted as <identity>:<hex-secret>, where <identity>
is PSK identity, and <hex-secret> is secret in hex. An
empty line, and line which starts with '#' are skipped.
The first identity and secret pair encountered is used.
The default enabled cipher list might not contain any
PSK cipher suite. In that case, desired PSK cipher
suites must be enabled using :option:`--client-ciphers` option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use :option:`--client-no-http2-cipher-black-list`
option. But be aware its implications.
HTTP/2 and SPDY
~~~~~~~~~~~~~~~
@@ -1134,7 +1182,7 @@ HTTP
Change server response header field value to <NAME>.
Default: ``nghttpx nghttp2/1.18.0``
Default: ``nghttpx nghttp2/1.19.0-DEV``
.. option:: --no-server-rewrite

View File

@@ -370,6 +370,81 @@ parameter in :option:`--backend` option, like so:
nghttpx will cache resolved addresses for certain period of time. To
change this cache period, use :option:`--dns-cache-timeout`.
Enable PROXY protocol
---------------------
PROXY protocol can be enabled per frontend. In order to enable PROXY
protocol, use ``proxyproto`` parameter in :option:`--frontend` option,
like so:
.. code-block:: text
frontend=*,443;proxyproto
PSK cipher suites
-----------------
nghttpx supports pre-shared key (PSK) cipher suites for both frontend
and backend TLS connections. For frontend connection, use
:option:`--psk-secrets` option to specify a file which contains PSK
identity and secrets. The format of the file is
``<identity>:<hex-secret>``, where ``<identity>`` is PSK identity, and
``<hex-secret>`` is PSK secret in hex, like so:
.. code-block:: text
client1:9567800e065e078085c241d54a01c6c3f24b3bab71a606600f4c6ad2c134f3b9
client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
nghttpx server accepts any of the identity and secret pairs in the
file. The default cipher suite list does not contain PSK cipher
suites. In order to use PSK, PSK cipher suite must be enabled by
using :option:`--ciphers` option. The desired PSK cipher suite may be
listed in `HTTP/2 cipher black list
<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
using :option:`--no-http2-cipher-black-list` option. But you should
understand its implications.
At the time of writing, even if only PSK cipher suites are specified
in :option:`--ciphers` option, certificate and private key are still
required.
For backend connection, use :option:`--client-psk-secrets` option to
specify a file which contains single PSK identity and secret. The
format is the same as the file used by :option:`--psk-secrets`
described above, but only first identity and secret pair is solely
used, like so:
.. code-block:: text
client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
The default cipher suite list does not contain PSK cipher suites. In
order to use PSK, PSK cipher suite must be enabled by using
:option:`--client-ciphers` option. The desired PSK cipher suite may
be listed in `HTTP/2 cipher black list
<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
using :option:`--client-no-http2-cipher-black-list` option. But you
should understand its implications.
Migration from nghttpx v1.18.x or earlier
-----------------------------------------
As of nghttpx v1.19.0, :option:`--ciphers` option only changes cipher
list for frontend TLS connection. In order to change cipher list for
backend connection, use :option:`--client-ciphers` option.
Similarly, :option:`--no-http2-cipher-black-list` option only disables
HTTP/2 cipher black list for frontend connection. In order to disable
HTTP/2 cipher black list for backend connection, use
:option:`--client-no-http2-cipher-black-list` option.
``--accept-proxy-protocol`` option was deprecated. Instead, use
``proxyproto`` parameter in :option:`--frontend` option to enable
PROXY protocol support per frontend.
Migration from nghttpx v1.8.0 or earlier
----------------------------------------

View File

@@ -287,7 +287,7 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
int rv;
if (session_data->stream_data->stream_id == stream_id) {
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
fprintf(stderr, "Stream %d closed with error_code=%u\n", stream_id,
error_code);
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
if (rv != 0) {

View File

@@ -153,6 +153,10 @@ OPTIONS = [
"dns-lookup-timeout",
"dns-max-try",
"frontend-keep-alive-timeout",
"psk-secrets",
"client-psk-secrets",
"client-no-http2-cipher-black-list",
"client-ciphers",
]
LOGVARS = [

View File

@@ -103,6 +103,7 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
backendTLS := false
dns := false
externalDNS := false
acceptProxyProtocol := false
for _, k := range src_args {
switch k {
case "--http2-bridge":
@@ -112,6 +113,8 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
case "--external-dns":
dns = true
externalDNS = true
case "--accept-proxy-protocol":
acceptProxyProtocol = true
default:
args = append(args, k)
}
@@ -160,12 +163,17 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
b += ";dns"
}
noTLS := "no-tls"
noTLS := ";no-tls"
if frontendTLS {
noTLS = ""
}
args = append(args, fmt.Sprintf("-f127.0.0.1,%v;%v", serverPort, noTLS), b,
var proxyProto string
if acceptProxyProtocol {
proxyProto = ";proxyproto"
}
args = append(args, fmt.Sprintf("-f127.0.0.1,%v%v%v", serverPort, noTLS, proxyProto), b,
"--errorlog-file="+logDir+"/log.txt", "-LINFO")
authority := fmt.Sprintf("127.0.0.1:%v", connectPort)

View File

@@ -2281,10 +2281,6 @@ ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin,
return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix);
}
static size_t hd_get_num_table_entries(nghttp2_hd_context *context) {
return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH;
}
static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context,
size_t idx) {
if (idx == 0) {
@@ -2301,7 +2297,7 @@ static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context,
}
size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) {
return hd_get_num_table_entries(&deflater->ctx);
return get_max_index(&deflater->ctx);
}
const nghttp2_nv *
@@ -2320,7 +2316,7 @@ nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) {
}
size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) {
return hd_get_num_table_entries(&inflater->ctx);
return get_max_index(&inflater->ctx);
}
const nghttp2_nv *

View File

@@ -365,7 +365,7 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
return promised_stream_id;
}
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags _U_,
int32_t stream_id,
int32_t window_size_increment) {
int rv;
@@ -373,7 +373,6 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
if (window_size_increment == 0) {
return 0;
}
flags = 0;
if (stream_id == 0) {
rv = nghttp2_adjust_local_window_size(
&session->local_window_size, &session->recv_window_size,
@@ -404,14 +403,14 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
nghttp2_max(0, stream->consumed_size - window_size_increment);
}
return nghttp2_session_add_window_update(session, flags, stream_id,
return nghttp2_session_add_window_update(session, 0, stream_id,
window_size_increment);
}
return 0;
}
int nghttp2_session_set_local_window_size(nghttp2_session *session,
uint8_t flags, int32_t stream_id,
uint8_t flags _U_, int32_t stream_id,
int32_t window_size) {
int32_t window_size_increment;
nghttp2_stream *stream;
@@ -421,8 +420,6 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
flags = 0;
if (stream_id == 0) {
window_size_increment = window_size - session->local_window_size;
@@ -472,7 +469,7 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
}
if (window_size_increment > 0) {
return nghttp2_session_add_window_update(session, flags, stream_id,
return nghttp2_session_add_window_update(session, 0, stream_id,
window_size_increment);
}

View File

@@ -557,7 +557,7 @@ Http2Handler::~Http2Handler() {
on_session_closed(this, session_id_);
nghttp2_session_del(session_);
if (ssl_) {
SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);
SSL_set_shutdown(ssl_, SSL_get_shutdown(ssl_) | SSL_RECEIVED_SHUTDOWN);
ERR_clear_error();
SSL_shutdown(ssl_);
}
@@ -2121,6 +2121,7 @@ int HttpServer::run() {
SSL_CTX_set_options(ssl_ctx, ssl_opts);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_max_version(ssl_ctx, TLS1_3_VERSION);
if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;

View File

@@ -373,7 +373,7 @@ OPTIONS:
<< std::endl;
}
static struct option long_options[] = {
constexpr static struct option long_options[] = {
{"http1text", no_argument, nullptr, 't'},
{"table-size", required_argument, nullptr, 's'},
{"deflate-table-size", required_argument, nullptr, 'S'},

View File

@@ -79,7 +79,8 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
} // namespace
Config::Config()
: data_length(-1),
: ciphers(ssl::DEFAULT_CIPHER_LIST),
data_length(-1),
addrs(nullptr),
nreqs(1),
nclients(1),
@@ -509,7 +510,7 @@ void Client::disconnect() {
ev_io_stop(worker->loop, &wev);
ev_io_stop(worker->loop, &rev);
if (ssl) {
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
ERR_clear_error();
if (SSL_shutdown(ssl) != 1) {
@@ -595,7 +596,8 @@ void print_server_tmp_key(SSL *ssl) {
std::cout << "Server Temp Key: ";
switch (EVP_PKEY_id(key)) {
auto pkey_id = EVP_PKEY_id(key);
switch (pkey_id) {
case EVP_PKEY_RSA:
std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl;
break;
@@ -615,6 +617,10 @@ void print_server_tmp_key(SSL *ssl) {
<< std::endl;
break;
}
default:
std::cout << OBJ_nid2sn(pkey_id) << " " << EVP_PKEY_bits(key) << " bits"
<< std::endl;
break;
}
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
}
@@ -1697,6 +1703,8 @@ Options:
--ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Default: )"
<< config.ciphers << R"(
-p, --no-tls-proto=<PROTOID>
Specify ALPN identifier of the protocol to be used when
accessing http URI without SSL/TLS.)";
@@ -1831,7 +1839,7 @@ int main(int argc, char **argv) {
bool nreqs_set_manually = false;
while (1) {
static int flag = 0;
static option long_options[] = {
constexpr static option long_options[] = {
{"requests", required_argument, nullptr, 'n'},
{"clients", required_argument, nullptr, 'c'},
{"data", required_argument, nullptr, 'd'},
@@ -2240,16 +2248,10 @@ int main(int argc, char **argv) {
SSL_CTX_set_options(ssl_ctx, ssl_opts);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_max_version(ssl_ctx, TLS1_3_VERSION);
const char *ciphers;
if (config.ciphers.empty()) {
ciphers = ssl::DEFAULT_CIPHER_LIST;
} else {
ciphers = config.ciphers.c_str();
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) {
std::cerr << "SSL_CTX_set_cipher_list with " << ciphers
if (SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers.c_str()) == 0) {
std::cerr << "SSL_CTX_set_cipher_list with " << config.ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr)
<< std::endl;
exit(EXIT_FAILURE);

View File

@@ -146,7 +146,7 @@ int htp_body_cb(http_parser *htp, const char *data, size_t len) {
} // namespace
namespace {
http_parser_settings htp_hooks = {
constexpr http_parser_settings htp_hooks = {
htp_msg_begincb, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
htp_statuscb, // http_data_cb on_status;

View File

@@ -113,7 +113,7 @@ ssize_t send_callback(spdylay_session *session, const uint8_t *data,
auto &wb = client->wb;
if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) {
return SPDYLAY_ERR_DEFERRED;
return SPDYLAY_ERR_WOULDBLOCK;
}
return wb.append(data, length);

View File

@@ -251,7 +251,7 @@ OPTIONS:
;
}
static struct option long_options[] = {
constexpr static struct option long_options[] = {
{"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}};
int main(int argc, char **argv) {

View File

@@ -434,6 +434,102 @@ inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) {
return iovcnt;
}
// MemchunkBuffer is similar to Buffer, but it uses pooled Memchunk
// for its underlying buffer.
template <typename Memchunk> struct MemchunkBuffer {
MemchunkBuffer(Pool<Memchunk> *pool) : pool(pool), chunk(nullptr) {}
MemchunkBuffer(const MemchunkBuffer &) = delete;
MemchunkBuffer(MemchunkBuffer &&other) noexcept
: pool(other.pool), chunk(other.chunk) {
other.chunk = nullptr;
}
MemchunkBuffer &operator=(const MemchunkBuffer &) = delete;
MemchunkBuffer &operator=(MemchunkBuffer &&other) noexcept {
if (this == &other) {
return *this;
}
pool = other.pool;
chunk = other.chunk;
other.chunk = nullptr;
return *this;
}
~MemchunkBuffer() {
if (!pool || !chunk) {
return;
}
pool->recycle(chunk);
}
// Ensures that the underlying buffer is allocated.
void ensure_chunk() {
if (chunk) {
return;
}
chunk = pool->get();
}
// Releases the underlying buffer.
void release_chunk() {
if (!chunk) {
return;
}
pool->recycle(chunk);
chunk = nullptr;
}
// Returns true if the underlying buffer is allocated.
bool chunk_avail() const { return chunk != nullptr; }
// The functions below must be called after the underlying buffer is
// allocated (use ensure_chunk).
// MemchunkBuffer provides the same interface functions with Buffer.
// Since we has chunk as a member variable, pos and last are
// implemented as wrapper functions.
uint8_t *pos() const { return chunk->pos; }
uint8_t *last() const { return chunk->last; }
size_t rleft() const { return chunk->len(); }
size_t wleft() const { return chunk->left(); }
size_t write(const void *src, size_t count) {
count = std::min(count, wleft());
auto p = static_cast<const uint8_t *>(src);
chunk->last = std::copy_n(p, count, chunk->last);
return count;
}
size_t write(size_t count) {
count = std::min(count, wleft());
chunk->last += count;
return count;
}
size_t drain(size_t count) {
count = std::min(count, rleft());
chunk->pos += count;
return count;
}
size_t drain_reset(size_t count) {
count = std::min(count, rleft());
std::copy(chunk->pos + count, chunk->last, std::begin(chunk->buf));
chunk->last = std::begin(chunk->buf) + (chunk->last - (chunk->pos + count));
chunk->pos = std::begin(chunk->buf);
return count;
}
void reset() { chunk->reset(); }
uint8_t *begin() { return std::begin(chunk->buf); }
uint8_t &operator[](size_t n) { return chunk->buf[n]; }
const uint8_t &operator[](size_t n) const { return chunk->buf[n]; }
Pool<Memchunk> *pool;
Memchunk *chunk;
};
using DefaultMemchunkBuffer = MemchunkBuffer<Memchunk16K>;
} // namespace nghttp2
#endif // MEMCHUNK_H

View File

@@ -371,7 +371,7 @@ int htp_msg_completecb(http_parser *htp) {
} // namespace
namespace {
http_parser_settings htp_hooks = {
constexpr http_parser_settings htp_hooks = {
htp_msg_begincb, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
htp_statuscb, // http_data_cb on_status;
@@ -701,7 +701,7 @@ void HttpClient::disconnect() {
session = nullptr;
if (ssl) {
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
ERR_clear_error();
SSL_shutdown(ssl);
SSL_free(ssl);
@@ -2212,6 +2212,7 @@ int communicate(
SSL_CTX_set_options(ssl_ctx, ssl_opts);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_max_version(ssl_ctx, TLS1_3_VERSION);
if (SSL_CTX_set_cipher_list(ssl_ctx, CIPHER_LIST) == 0) {
std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
<< std::endl;
@@ -2680,7 +2681,7 @@ int main(int argc, char **argv) {
bool color = false;
while (1) {
static int flag = 0;
static option long_options[] = {
constexpr static option long_options[] = {
{"verbose", no_argument, nullptr, 'v'},
{"null-out", no_argument, nullptr, 'n'},
{"remote-name", no_argument, nullptr, 'O'},

View File

@@ -202,7 +202,7 @@ int main(int argc, char **argv) {
while (1) {
static int flag = 0;
static option long_options[] = {
constexpr static option long_options[] = {
{"address", required_argument, nullptr, 'a'},
{"daemon", no_argument, nullptr, 'D'},
{"htdocs", required_argument, nullptr, 'd'},

View File

@@ -181,6 +181,9 @@ int main(int argc, char *argv[]) {
!CU_add_test(pSuite, "util_random_alpha_digit",
shrpx::test_util_random_alpha_digit) ||
!CU_add_test(pSuite, "util_format_hex", shrpx::test_util_format_hex) ||
!CU_add_test(pSuite, "util_is_hex_string",
shrpx::test_util_is_hex_string) ||
!CU_add_test(pSuite, "util_decode_hex", shrpx::test_util_decode_hex) ||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
!CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) ||
!CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||

View File

@@ -487,7 +487,7 @@ void exec_binary() {
}
// restores original stderr
util::restore_original_fds();
restore_original_fds();
if (execve(argv[0], argv.get(), envp.get()) == -1) {
auto error = errno;
@@ -1354,6 +1354,9 @@ void fill_default_config(Config *config) {
}
tlsconf.session_timeout = std::chrono::hours(12);
tlsconf.ciphers = StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
tlsconf.client.ciphers =
StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
#if OPENSSL_1_1_API
tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
#else // !OPENSSL_1_1_API
@@ -1694,6 +1697,10 @@ Connections:
default. Any requests which come through this address
are replied with 200 HTTP status, without no body.
To accept PROXY protocol version 1 on frontend
connection, specify "proxyproto" parameter. This is
disabled by default.
Default: *,3000
--backlog=<N>
Set listen backlog size.
@@ -1718,8 +1725,6 @@ Connections:
timeouts when connecting and making CONNECT request can
be specified by --backend-read-timeout and
--backend-write-timeout options.
--accept-proxy-protocol
Accept PROXY protocol version 1 on frontend connection.
Performance:
-n, --workers=<N>
@@ -1894,8 +1899,15 @@ Timeout:
SSL/TLS:
--ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
Default: )"
<< config->tls.ciphers << R"(
--client-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
Default: )"
<< config->tls.client.ciphers << R"(
--ecdh-curves=<LIST>
Set supported curve list for frontend connections.
<LIST> is a colon separated list of curve NID or names
@@ -2089,9 +2101,15 @@ SSL/TLS:
Default: )"
<< util::duration_str(config->tls.dyn_rec.idle_timeout) << R"(
--no-http2-cipher-black-list
Allow black listed cipher suite on HTTP/2 connection.
See https://tools.ietf.org/html/rfc7540#appendix-A for
the complete HTTP/2 cipher suites black list.
Allow black listed cipher suite on frontend HTTP/2
connection. See
https://tools.ietf.org/html/rfc7540#appendix-A for the
complete HTTP/2 cipher suites black list.
--client-no-http2-cipher-black-list
Allow black listed cipher suite on backend HTTP/2
connection. See
https://tools.ietf.org/html/rfc7540#appendix-A for the
complete HTTP/2 cipher suites black list.
--tls-sct-dir=<DIR>
Specifies the directory where *.sct files exist. All
*.sct files in <DIR> are read, and sent as
@@ -2101,6 +2119,33 @@ SSL/TLS:
argument <CERT>, or certificate option in configuration
file. For additional certificates, use --subcert
option. This option requires OpenSSL >= 1.0.2.
--psk-secrets=<PATH>
Read list of PSK identity and secrets from <PATH>. This
is used for frontend connection. The each line of input
file is formatted as <identity>:<hex-secret>, where
<identity> is PSK identity, and <hex-secret> is secret
in hex. An empty line, and line which starts with '#'
are skipped. The default enabled cipher list might not
contain any PSK cipher suite. In that case, desired PSK
cipher suites must be enabled using --ciphers option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use --no-http2-cipher-black-list option.
But be aware its implications.
--client-psk-secrets=<PATH>
Read PSK identity and secrets from <PATH>. This is used
for backend connection. The each line of input file is
formatted as <identity>:<hex-secret>, where <identity>
is PSK identity, and <hex-secret> is secret in hex. An
empty line, and line which starts with '#' are skipped.
The first identity and secret pair encountered is used.
The default enabled cipher list might not contain any
PSK cipher suite. In that case, desired PSK cipher
suites must be enabled using --client-ciphers option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use --client-no-http2-cipher-black-list
option. But be aware its implications.
HTTP/2 and SPDY:
-c, --frontend-http2-max-concurrent-streams=<N>
@@ -2833,7 +2878,7 @@ int main(int argc, char **argv) {
fill_default_config(mod_config());
// make copy of stderr
util::store_original_fds();
store_original_fds();
// First open log files with default configuration, so that we can
// log errors/warnings while reading configuration files.
@@ -2866,7 +2911,7 @@ int main(int argc, char **argv) {
while (1) {
static int flag = 0;
static option long_options[] = {
static constexpr option long_options[] = {
{SHRPX_OPT_DAEMON.c_str(), no_argument, nullptr, 'D'},
{SHRPX_OPT_LOG_LEVEL.c_str(), required_argument, nullptr, 'L'},
{SHRPX_OPT_BACKEND.c_str(), required_argument, nullptr, 'b'},
@@ -3076,6 +3121,11 @@ int main(int argc, char **argv) {
{SHRPX_OPT_DNS_MAX_TRY.c_str(), required_argument, &flag, 145},
{SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument,
&flag, 146},
{SHRPX_OPT_PSK_SECRETS.c_str(), required_argument, &flag, 147},
{SHRPX_OPT_CLIENT_PSK_SECRETS.c_str(), required_argument, &flag, 148},
{SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST.c_str(), no_argument,
&flag, 149},
{SHRPX_OPT_CLIENT_CIPHERS.c_str(), required_argument, &flag, 150},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
@@ -3766,6 +3816,23 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT,
StringRef{optarg});
break;
case 147:
// --psk-secrets
cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
break;
case 148:
// --client-psk-secrets
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, StringRef{optarg});
break;
case 149:
// --client-no-http2-cipher-black-list
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
StringRef::from_lit("yes"));
break;
case 150:
// --client-ciphers
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CIPHERS, StringRef{optarg});
break;
default:
break;
}

View File

@@ -117,6 +117,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
int ClientHandler::noop() { return 0; }
int ClientHandler::read_clear() {
rb_.ensure_chunk();
for (;;) {
if (rb_.rleft() && on_read() != 0) {
return -1;
@@ -132,9 +133,12 @@ int ClientHandler::read_clear() {
return 0;
}
auto nread = conn_.read_clear(rb_.last, rb_.wleft());
auto nread = conn_.read_clear(rb_.last(), rb_.wleft());
if (nread == 0) {
if (rb_.rleft() == 0) {
rb_.release_chunk();
}
return 0;
}
@@ -209,6 +213,8 @@ int ClientHandler::tls_handshake() {
int ClientHandler::read_tls() {
ERR_clear_error();
rb_.ensure_chunk();
for (;;) {
// we should process buffered data first before we read EOF.
if (rb_.rleft() && on_read() != 0) {
@@ -225,9 +231,12 @@ int ClientHandler::read_tls() {
return 0;
}
auto nread = conn_.read_tls(rb_.last, rb_.wleft());
auto nread = conn_.read_tls(rb_.last(), rb_.wleft());
if (nread == 0) {
if (rb_.rleft() == 0) {
rb_.release_chunk();
}
return 0;
}
@@ -303,7 +312,7 @@ int ClientHandler::upstream_write() {
int ClientHandler::upstream_http2_connhd_read() {
auto nread = std::min(left_connhd_len_, rb_.rleft());
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
rb_.pos, nread) != 0) {
rb_.pos(), nread) != 0) {
// There is no downgrade path here. Just drop the connection.
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "invalid client connection header";
@@ -332,7 +341,7 @@ int ClientHandler::upstream_http2_connhd_read() {
int ClientHandler::upstream_http1_connhd_read() {
auto nread = std::min(left_connhd_len_, rb_.rleft());
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
rb_.pos, nread) != 0) {
rb_.pos(), nread) != 0) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "This is HTTP/1.1 connection, "
<< "but may be upgraded to HTTP/2 later.";
@@ -386,6 +395,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
// so the required space is 64 + 48 + 16 + 48 + 16 + 16 + 16 +
// 32 + 8 + 8 * 8 = 328.
balloc_(512, 512),
rb_(worker->get_mcpool()),
conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(),
get_config()->conn.upstream.timeout.write,
get_config()->conn.upstream.timeout.read,
@@ -413,7 +423,8 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
auto config = get_config();
if (config->conn.upstream.accept_proxy_protocol) {
if (faddr_->accept_proxy_protocol ||
config->conn.upstream.accept_proxy_protocol) {
read_ = &ClientHandler::read_clear;
write_ = &ClientHandler::noop;
on_read_ = &ClientHandler::proxy_protocol_read;
@@ -646,9 +657,11 @@ int ClientHandler::do_read() { return read_(*this); }
int ClientHandler::do_write() { return write_(*this); }
int ClientHandler::on_read() {
auto rv = on_read_(*this);
if (rv != 0) {
return rv;
if (rb_.chunk_avail()) {
auto rv = on_read_(*this);
if (rv != 0) {
return rv;
}
}
conn_.handle_tls_pending_read();
return 0;
@@ -1324,7 +1337,7 @@ ssize_t parse_proxy_line_port(const uint8_t *first, const uint8_t *last) {
int ClientHandler::on_proxy_protocol_finish() {
if (conn_.tls.ssl) {
conn_.tls.rbuf.append(rb_.pos, rb_.rleft());
conn_.tls.rbuf.append(rb_.pos(), rb_.rleft());
rb_.reset();
}
@@ -1345,7 +1358,7 @@ int ClientHandler::proxy_protocol_read() {
CLOG(INFO, this) << "PROXY-protocol: Started";
}
auto first = rb_.pos;
auto first = rb_.pos();
// NULL character really destroys functions which expects NULL
// terminated string. We won't expect it in PROXY protocol line, so
@@ -1354,12 +1367,12 @@ int ClientHandler::proxy_protocol_read() {
constexpr size_t MAX_PROXY_LINELEN = 107;
auto bufend = rb_.pos + std::min(MAX_PROXY_LINELEN, rb_.rleft());
auto bufend = rb_.pos() + std::min(MAX_PROXY_LINELEN, rb_.rleft());
auto end =
std::find_first_of(rb_.pos, bufend, std::begin(chrs), std::end(chrs));
std::find_first_of(rb_.pos(), bufend, std::begin(chrs), std::end(chrs));
if (end == bufend || *end == '\0' || end == rb_.pos || *(end - 1) != '\r') {
if (end == bufend || *end == '\0' || end == rb_.pos() || *(end - 1) != '\r') {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: No ending CR LF sequence found";
}
@@ -1370,14 +1383,14 @@ int ClientHandler::proxy_protocol_read() {
constexpr auto HEADER = StringRef::from_lit("PROXY ");
if (static_cast<size_t>(end - rb_.pos) < HEADER.size()) {
if (static_cast<size_t>(end - rb_.pos()) < HEADER.size()) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: PROXY version 1 ID not found";
}
return -1;
}
if (!util::streq(HEADER, StringRef{rb_.pos, HEADER.size()})) {
if (!util::streq(HEADER, StringRef{rb_.pos(), HEADER.size()})) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Bad PROXY protocol version 1 ID";
}
@@ -1388,22 +1401,22 @@ int ClientHandler::proxy_protocol_read() {
int family;
if (rb_.pos[0] == 'T') {
if (end - rb_.pos < 5) {
if (rb_.pos()[0] == 'T') {
if (end - rb_.pos() < 5) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
}
return -1;
}
if (rb_.pos[1] != 'C' || rb_.pos[2] != 'P') {
if (rb_.pos()[1] != 'C' || rb_.pos()[2] != 'P') {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
}
return -1;
}
switch (rb_.pos[3]) {
switch (rb_.pos()[3]) {
case '4':
family = AF_INET;
break;
@@ -1419,26 +1432,26 @@ int ClientHandler::proxy_protocol_read() {
rb_.drain(5);
} else {
if (end - rb_.pos < 7) {
if (end - rb_.pos() < 7) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
}
return -1;
}
if (!util::streq_l("UNKNOWN", rb_.pos, 7)) {
if (!util::streq_l("UNKNOWN", rb_.pos(), 7)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
}
return -1;
}
rb_.drain(end + 2 - rb_.pos);
rb_.drain(end + 2 - rb_.pos());
return on_proxy_protocol_finish();
}
// source address
auto token_end = std::find(rb_.pos, end, ' ');
auto token_end = std::find(rb_.pos(), end, ' ');
if (token_end == end) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Source address not found";
@@ -1447,20 +1460,20 @@ int ClientHandler::proxy_protocol_read() {
}
*token_end = '\0';
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos()), family)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source address";
}
return -1;
}
auto src_addr = rb_.pos;
auto src_addrlen = token_end - rb_.pos;
auto src_addr = rb_.pos();
auto src_addrlen = token_end - rb_.pos();
rb_.drain(token_end - rb_.pos + 1);
rb_.drain(token_end - rb_.pos() + 1);
// destination address
token_end = std::find(rb_.pos, end, ' ');
token_end = std::find(rb_.pos(), end, ' ');
if (token_end == end) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Destination address not found";
@@ -1469,7 +1482,7 @@ int ClientHandler::proxy_protocol_read() {
}
*token_end = '\0';
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos()), family)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination address";
}
@@ -1478,26 +1491,26 @@ int ClientHandler::proxy_protocol_read() {
// Currently we don't use destination address
rb_.drain(token_end - rb_.pos + 1);
rb_.drain(token_end - rb_.pos() + 1);
// source port
auto n = parse_proxy_line_port(rb_.pos, end);
if (n <= 0 || *(rb_.pos + n) != ' ') {
auto n = parse_proxy_line_port(rb_.pos(), end);
if (n <= 0 || *(rb_.pos() + n) != ' ') {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source port";
}
return -1;
}
rb_.pos[n] = '\0';
auto src_port = rb_.pos;
rb_.pos()[n] = '\0';
auto src_port = rb_.pos();
auto src_portlen = n;
rb_.drain(n + 1);
// destination port
n = parse_proxy_line_port(rb_.pos, end);
if (n <= 0 || rb_.pos + n != end) {
n = parse_proxy_line_port(rb_.pos(), end);
if (n <= 0 || rb_.pos() + n != end) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination port";
}
@@ -1506,14 +1519,14 @@ int ClientHandler::proxy_protocol_read() {
// Currently we don't use destination port
rb_.drain(end + 2 - rb_.pos);
rb_.drain(end + 2 - rb_.pos());
ipaddr_ =
make_string_ref(balloc_, StringRef{src_addr, src_addr + src_addrlen});
port_ = make_string_ref(balloc_, StringRef{src_port, src_port + src_portlen});
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos - first)
CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos() - first)
<< " bytes read";
}

View File

@@ -124,7 +124,7 @@ public:
int64_t body_bytes_sent);
Worker *get_worker() const;
using ReadBuf = Buffer<16_k>;
using ReadBuf = DefaultMemchunkBuffer;
ReadBuf *get_rb();
@@ -171,6 +171,7 @@ private:
// sure that the allocations must be bounded, and not proportional
// to the number of requests.
BlockAllocator balloc_;
DefaultMemchunkBuffer rb_;
Connection conn_;
ev_timer reneg_shutdown_timer_;
std::unique_ptr<Upstream> upstream_;
@@ -197,7 +198,6 @@ private:
bool should_close_after_write_;
// true if affinity_hash_ is computed
bool affinity_hash_computed_;
ReadBuf rb_;
};
} // namespace shrpx

View File

@@ -677,6 +677,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out,
struct UpstreamParams {
int alt_mode;
bool tls;
bool proxyproto;
};
namespace {
@@ -705,6 +706,8 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
return -1;
}
out.alt_mode = ALTMODE_HEALTHMON;
} else if (util::strieq_l("proxyproto", param)) {
out.proxyproto = true;
} else if (!param.empty()) {
LOG(ERROR) << "frontend: " << param << ": unknown keyword";
return -1;
@@ -1196,6 +1199,130 @@ int read_tls_sct_from_dir(std::vector<uint8_t> &dst, const StringRef &opt,
}
} // namespace
namespace {
// Reads PSK secrets from path, and parses each line. The result is
// directly stored into config->tls.psk_secrets. This function
// returns 0 if it succeeds, or -1.
int parse_psk_secrets(Config *config, const StringRef &path) {
auto &tlsconf = config->tls;
std::ifstream f(path.c_str(), std::ios::binary);
if (!f) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": could not open file " << path;
return -1;
}
size_t lineno = 0;
std::string line;
while (std::getline(f, line)) {
++lineno;
if (line.empty() || line[0] == '#') {
continue;
}
auto sep_it = std::find(std::begin(line), std::end(line), ':');
if (sep_it == std::end(line)) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
<< ": could not fine separator at line " << lineno;
return -1;
}
if (sep_it == std::begin(line)) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty identity at line "
<< lineno;
return -1;
}
if (sep_it + 1 == std::end(line)) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty secret at line "
<< lineno;
return -1;
}
if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
<< ": secret must be hex string at line " << lineno;
return -1;
}
auto identity =
make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
auto secret =
util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
auto rv = tlsconf.psk_secrets.emplace(identity, secret);
if (!rv.second) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
<< ": identity has already been registered at line " << lineno;
return -1;
}
}
return 0;
}
} // namespace
namespace {
// Reads PSK secrets from path, and parses each line. The result is
// directly stored into config->tls.client.psk. This function returns
// 0 if it succeeds, or -1.
int parse_client_psk_secrets(Config *config, const StringRef &path) {
auto &tlsconf = config->tls;
std::ifstream f(path.c_str(), std::ios::binary);
if (!f) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not open file "
<< path;
return -1;
}
size_t lineno = 0;
std::string line;
while (std::getline(f, line)) {
++lineno;
if (line.empty() || line[0] == '#') {
continue;
}
auto sep_it = std::find(std::begin(line), std::end(line), ':');
if (sep_it == std::end(line)) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
<< ": could not find separator at line " << lineno;
return -1;
}
if (sep_it == std::begin(line)) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty identity at line "
<< lineno;
return -1;
}
if (sep_it + 1 == std::end(line)) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty secret at line "
<< lineno;
return -1;
}
if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
<< ": secret must be hex string at line " << lineno;
return -1;
}
tlsconf.client.psk.identity =
make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
tlsconf.client.psk.secret =
util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
return 0;
}
return 0;
}
} // namespace
// generated by gennghttpxfun.py
int option_lookup_token(const char *name, size_t namelen) {
switch (namelen) {
@@ -1363,6 +1490,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("ecdh-curve", name, 10)) {
return SHRPX_OPTID_ECDH_CURVES;
}
if (util::strieq_l("psk-secret", name, 10)) {
return SHRPX_OPTID_PSK_SECRETS;
}
break;
case 't':
if (util::strieq_l("write-burs", name, 10)) {
@@ -1455,6 +1585,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("backend-no-tl", name, 13)) {
return SHRPX_OPTID_BACKEND_NO_TLS;
}
if (util::strieq_l("client-cipher", name, 13)) {
return SHRPX_OPTID_CLIENT_CIPHERS;
}
break;
case 't':
if (util::strieq_l("tls-proto-lis", name, 13)) {
@@ -1550,6 +1683,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_ADD_REQUEST_HEADER;
}
break;
case 's':
if (util::strieq_l("client-psk-secret", name, 17)) {
return SHRPX_OPTID_CLIENT_PSK_SECRETS;
}
break;
case 't':
if (util::strieq_l("dns-lookup-timeou", name, 17)) {
return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
@@ -1869,6 +2007,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL;
}
break;
case 't':
if (util::strieq_l("client-no-http2-cipher-black-lis", name, 32)) {
return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST;
}
break;
}
break;
case 34:
@@ -2091,6 +2234,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
addr.fd = -1;
addr.tls = params.tls;
addr.alt_mode = params.alt_mode;
addr.accept_proxy_protocol = params.proxyproto;
if (addr.alt_mode == ALTMODE_API) {
apiconf.enabled = true;
@@ -2883,6 +3027,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
#endif // !HAVE_MRUBY
return 0;
case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL:
LOG(WARN) << opt << ": deprecated. Use proxyproto keyword in "
<< SHRPX_OPT_FRONTEND << " instead.";
config->conn.upstream.accept_proxy_protocol = util::strieq_l("yes", optarg);
return 0;
@@ -3132,6 +3278,19 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT:
return parse_duration(&config->conn.upstream.timeout.idle_read, opt,
optarg);
case SHRPX_OPTID_PSK_SECRETS:
return parse_psk_secrets(config, optarg);
case SHRPX_OPTID_CLIENT_PSK_SECRETS:
return parse_client_psk_secrets(config, optarg);
case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST:
config->tls.client.no_http2_cipher_black_list =
util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_CLIENT_CIPHERS:
config->tls.client.ciphers = make_string_ref(config->balloc, optarg);
return 0;
case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored";

View File

@@ -319,6 +319,12 @@ constexpr auto SHRPX_OPT_DNS_LOOKUP_TIMEOUT =
constexpr auto SHRPX_OPT_DNS_MAX_TRY = StringRef::from_lit("dns-max-try");
constexpr auto SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT =
StringRef::from_lit("frontend-keep-alive-timeout");
constexpr auto SHRPX_OPT_PSK_SECRETS = StringRef::from_lit("psk-secrets");
constexpr auto SHRPX_OPT_CLIENT_PSK_SECRETS =
StringRef::from_lit("client-psk-secrets");
constexpr auto SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST =
StringRef::from_lit("client-no-http2-cipher-black-list");
constexpr auto SHRPX_OPT_CLIENT_CIPHERS = StringRef::from_lit("client-ciphers");
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
@@ -382,6 +388,8 @@ struct UpstreamAddr {
bool host_unix;
// true if TLS is enabled.
bool tls;
// true if client is supposed to send PROXY protocol v1 header.
bool accept_proxy_protocol;
int fd;
};
@@ -541,12 +549,23 @@ struct TLSConfig {
bool enabled;
} client_verify;
// Client private key and certificate used in backend connections.
// Client (backend connection) TLS configuration.
struct {
// Client PSK configuration
struct {
// identity must be NULL terminated string.
StringRef identity;
StringRef secret;
} psk;
StringRef private_key_file;
StringRef cert_file;
StringRef ciphers;
bool no_http2_cipher_black_list;
} client;
// PSK secrets. The key is identity, and the associated value is
// its secret.
std::map<StringRef, StringRef> psk_secrets;
// The list of additional TLS certificate pair
std::vector<TLSCertificate> subcerts;
std::vector<unsigned char> alpn_prefs;
@@ -775,6 +794,7 @@ struct ConnectionConfig {
RateLimitConfig write;
} ratelimit;
size_t worker_connections;
// Deprecated. See UpstreamAddr.accept_proxy_protocol.
bool accept_proxy_protocol;
} upstream;
@@ -909,8 +929,11 @@ enum {
SHRPX_OPTID_CIPHERS,
SHRPX_OPTID_CLIENT,
SHRPX_OPTID_CLIENT_CERT_FILE,
SHRPX_OPTID_CLIENT_CIPHERS,
SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE,
SHRPX_OPTID_CLIENT_PROXY,
SHRPX_OPTID_CLIENT_PSK_SECRETS,
SHRPX_OPTID_CONF,
SHRPX_OPTID_DAEMON,
SHRPX_OPTID_DH_PARAM_FILE,
@@ -972,6 +995,7 @@ enum {
SHRPX_OPTID_PID_FILE,
SHRPX_OPTID_PRIVATE_KEY_FILE,
SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE,
SHRPX_OPTID_PSK_SECRETS,
SHRPX_OPTID_READ_BURST,
SHRPX_OPTID_READ_RATE,
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,

View File

@@ -95,7 +95,8 @@ Connection::~Connection() { disconnect(); }
void Connection::disconnect() {
if (tls.ssl) {
SSL_set_shutdown(tls.ssl, SSL_RECEIVED_SHUTDOWN);
SSL_set_shutdown(tls.ssl,
SSL_get_shutdown(tls.ssl) | SSL_RECEIVED_SHUTDOWN);
ERR_clear_error();
if (tls.cached_session) {
@@ -140,7 +141,10 @@ void Connection::disconnect() {
void Connection::prepare_client_handshake() { SSL_set_connect_state(tls.ssl); }
void Connection::prepare_server_handshake() { SSL_set_accept_state(tls.ssl); }
void Connection::prepare_server_handshake() {
SSL_set_accept_state(tls.ssl);
tls.server_handshake = true;
}
// BIO implementation is inspired by openldap implementation:
// http://www.openldap.org/devel/cvsweb.cgi/~checkout~/libraries/libldap/tls_o.c
@@ -529,7 +533,15 @@ int Connection::check_http2_requirement() {
}
return -1;
}
if (!get_config()->tls.no_http2_cipher_black_list &&
auto check_black_list = false;
if (tls.server_handshake) {
check_black_list = !get_config()->tls.no_http2_cipher_black_list;
} else {
check_black_list = !get_config()->tls.client.no_http2_cipher_black_list;
}
if (check_black_list &&
nghttp2::ssl::check_http2_cipher_black_list(tls.ssl)) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "The negotiated cipher suite is in HTTP/2 cipher suite "

View File

@@ -64,6 +64,8 @@ struct TLSConnection {
int handshake_state;
bool initial_handshake_done;
bool reneg_started;
// true if ssl is prepared to do handshake as server.
bool server_handshake;
};
struct TCPHint {

View File

@@ -99,6 +99,7 @@ namespace {
void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
auto resolv = static_cast<DNSResolver *>(w->data);
resolv->on_timeout();
process_result(resolv);
}
} // namespace
@@ -215,7 +216,9 @@ void DNSResolver::reset_timeout() {
if (tv == nullptr) {
return;
}
timer_.repeat = tv->tv_sec + tv->tv_usec / 1000000.;
// To avoid that timer_.repeat becomes 0, which makes ev_timer_again
// useless, add tiny fraction of time.
timer_.repeat = tv->tv_sec + tv->tv_usec / 1000000. + 1e-9;
ev_timer_again(loop_, &timer_);
}
@@ -295,7 +298,13 @@ void DNSResolver::on_result(int status, hostent *hostent) {
}
auto ap = *hostent->h_addr_list;
assert(ap);
if (!ap) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup for " << name_ << "failed: no address returned";
}
status_ = DNS_STATUS_ERROR;
return;
}
switch (hostent->h_addrtype) {
case AF_INET:

View File

@@ -638,7 +638,7 @@ int htp_hdrs_completecb(http_parser *htp) {
} // namespace
namespace {
http_parser_settings htp_hooks = {
constexpr http_parser_settings htp_hooks = {
nullptr, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
nullptr, // http_data_cb on_status;
@@ -1702,24 +1702,9 @@ int Http2Session::connection_made() {
return -1;
}
auto must_terminate =
addr_->tls && !nghttp2::ssl::check_http2_requirement(conn_.tls.ssl);
reset_connection_check_timer(CONNCHK_TIMEOUT);
if (must_terminate) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be negotiated.";
}
rv = terminate_session(NGHTTP2_INADEQUATE_SECURITY);
if (rv != 0) {
return -1;
}
} else {
submit_pending_requests();
}
submit_pending_requests();
signal_write();
return 0;

View File

@@ -1055,7 +1055,7 @@ int Http2Upstream::on_read() {
auto rlimit = handler_->get_rlimit();
if (rb->rleft()) {
rv = nghttp2_session_mem_recv(session_, rb->pos, rb->rleft());
rv = nghttp2_session_mem_recv(session_, rb->pos(), rb->rleft());
if (rv < 0) {
if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
ULOG(ERROR, this) << "nghttp2_session_mem_recv() returned error: "

View File

@@ -189,9 +189,9 @@ HttpDownstreamConnection::HttpDownstreamConnection(
readcb, connect_timeoutcb, this,
get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP1),
do_read_(&HttpDownstreamConnection::noop),
do_write_(&HttpDownstreamConnection::noop),
do_signal_write_(&HttpDownstreamConnection::noop),
on_read_(&HttpDownstreamConnection::noop),
on_write_(&HttpDownstreamConnection::noop),
signal_write_(&HttpDownstreamConnection::noop),
worker_(worker),
ssl_ctx_(worker->get_cl_ssl_ctx()),
group_(group),
@@ -454,7 +454,7 @@ int HttpDownstreamConnection::initiate_connection() {
ev_set_cb(&conn_.rev, readcb);
do_write_ = &HttpDownstreamConnection::write_reuse_first;
on_write_ = &HttpDownstreamConnection::write_reuse_first;
reuse_first_write_done_ = false;
}
@@ -1070,7 +1070,7 @@ int htp_msg_completecb(http_parser *htp) {
} // namespace
namespace {
http_parser_settings htp_hooks = {
constexpr http_parser_settings htp_hooks = {
htp_msg_begincb, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
nullptr, // http_data_cb on_status;
@@ -1096,9 +1096,9 @@ int HttpDownstreamConnection::write_reuse_first() {
}
if (conn_.tls.ssl) {
do_write_ = &HttpDownstreamConnection::write_tls;
on_write_ = &HttpDownstreamConnection::write_tls;
} else {
do_write_ = &HttpDownstreamConnection::write_clear;
on_write_ = &HttpDownstreamConnection::write_clear;
}
reuse_first_write_done_ = true;
@@ -1158,7 +1158,7 @@ int HttpDownstreamConnection::write_clear() {
// part of response body. So keep reading. Invoke read event
// to get read(2) error just in case.
ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
do_write_ = &HttpDownstreamConnection::noop;
on_write_ = &HttpDownstreamConnection::noop;
reusable_ = false;
break;
}
@@ -1216,15 +1216,15 @@ int HttpDownstreamConnection::tls_handshake() {
auto &connect_blocker = addr_->connect_blocker;
do_signal_write_ = &HttpDownstreamConnection::actual_signal_write;
signal_write_ = &HttpDownstreamConnection::actual_signal_write;
connect_blocker->on_success();
ev_set_cb(&conn_.rt, timeoutcb);
ev_set_cb(&conn_.wt, timeoutcb);
do_read_ = &HttpDownstreamConnection::read_tls;
do_write_ = &HttpDownstreamConnection::write_tls;
on_read_ = &HttpDownstreamConnection::read_tls;
on_write_ = &HttpDownstreamConnection::write_tls;
// TODO Check negotiated ALPN
@@ -1287,7 +1287,7 @@ int HttpDownstreamConnection::write_tls() {
// part of response body. So keep reading. Invoke read event
// to get read(2) error just in case.
ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
do_write_ = &HttpDownstreamConnection::noop;
on_write_ = &HttpDownstreamConnection::noop;
reusable_ = false;
break;
}
@@ -1407,32 +1407,32 @@ int HttpDownstreamConnection::connected() {
ev_set_cb(&conn_.wev, writecb);
if (conn_.tls.ssl) {
do_read_ = &HttpDownstreamConnection::tls_handshake;
do_write_ = &HttpDownstreamConnection::tls_handshake;
on_read_ = &HttpDownstreamConnection::tls_handshake;
on_write_ = &HttpDownstreamConnection::tls_handshake;
return 0;
}
do_signal_write_ = &HttpDownstreamConnection::actual_signal_write;
signal_write_ = &HttpDownstreamConnection::actual_signal_write;
connect_blocker->on_success();
ev_set_cb(&conn_.rt, timeoutcb);
ev_set_cb(&conn_.wt, timeoutcb);
do_read_ = &HttpDownstreamConnection::read_clear;
do_write_ = &HttpDownstreamConnection::write_clear;
on_read_ = &HttpDownstreamConnection::read_clear;
on_write_ = &HttpDownstreamConnection::write_clear;
return 0;
}
int HttpDownstreamConnection::on_read() { return do_read_(*this); }
int HttpDownstreamConnection::on_read() { return on_read_(*this); }
int HttpDownstreamConnection::on_write() { return do_write_(*this); }
int HttpDownstreamConnection::on_write() { return on_write_(*this); }
void HttpDownstreamConnection::on_upstream_change(Upstream *upstream) {}
void HttpDownstreamConnection::signal_write() { do_signal_write_(*this); }
void HttpDownstreamConnection::signal_write() { signal_write_(*this); }
int HttpDownstreamConnection::actual_signal_write() {
ev_feed_event(conn_.loop, &conn_.wev, EV_WRITE);

View File

@@ -91,8 +91,8 @@ public:
private:
Connection conn_;
std::function<int(HttpDownstreamConnection &)> do_read_, do_write_,
do_signal_write_;
std::function<int(HttpDownstreamConnection &)> on_read_, on_write_,
signal_write_;
Worker *worker_;
// nullptr if TLS is not used.
SSL_CTX *ssl_ctx_;

View File

@@ -505,7 +505,7 @@ int htp_msg_completecb(http_parser *htp) {
} // namespace
namespace {
http_parser_settings htp_hooks = {
constexpr http_parser_settings htp_hooks = {
htp_msg_begin, // http_cb on_message_begin;
htp_uricb, // http_data_cb on_url;
nullptr, // http_data_cb on_status;
@@ -532,7 +532,7 @@ int HttpsUpstream::on_read() {
// callback chain called by http_parser_execute()
if (downstream && downstream->get_upgraded()) {
auto rv = downstream->push_upload_data_chunk(rb->pos, rb->rleft());
auto rv = downstream->push_upload_data_chunk(rb->pos(), rb->rleft());
if (rv != 0) {
return -1;
@@ -565,8 +565,9 @@ int HttpsUpstream::on_read() {
}
// http_parser_execute() does nothing once it entered error state.
auto nread = http_parser_execute(
&htp_, &htp_hooks, reinterpret_cast<const char *>(rb->pos), rb->rleft());
auto nread = http_parser_execute(&htp_, &htp_hooks,
reinterpret_cast<const char *>(rb->pos()),
rb->rleft());
rb->drain(nread);
rlimit->startw();

View File

@@ -33,7 +33,11 @@
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif // HAVE_INTTYPES_H
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H
#include <sys/wait.h>
#include <cerrno>
@@ -410,7 +414,7 @@ int reopen_log_files() {
auto &errorconf = config->logging.error;
if (!accessconf.syslog && !accessconf.file.empty()) {
new_accesslog_fd = util::open_log_file(accessconf.file.c_str());
new_accesslog_fd = open_log_file(accessconf.file.c_str());
if (new_accesslog_fd == -1) {
LOG(ERROR) << "Failed to open accesslog file " << accessconf.file;
@@ -419,7 +423,7 @@ int reopen_log_files() {
}
if (!errorconf.syslog && !errorconf.file.empty()) {
new_errorlog_fd = util::open_log_file(errorconf.file.c_str());
new_errorlog_fd = open_log_file(errorconf.file.c_str());
if (new_errorlog_fd == -1) {
if (lgconf->errorlog_fd != -1) {
@@ -433,8 +437,8 @@ int reopen_log_files() {
}
}
util::close_log_file(lgconf->accesslog_fd);
util::close_log_file(lgconf->errorlog_fd);
close_log_file(lgconf->accesslog_fd);
close_log_file(lgconf->errorlog_fd);
lgconf->accesslog_fd = new_accesslog_fd;
lgconf->errorlog_fd = new_errorlog_fd;
@@ -478,4 +482,60 @@ void redirect_stderr_to_errorlog() {
dup2(lgconf->errorlog_fd, STDERR_FILENO);
}
namespace {
int STDERR_COPY = -1;
int STDOUT_COPY = -1;
} // namespace
void store_original_fds() {
// consider dup'ing stdout too
STDERR_COPY = dup(STDERR_FILENO);
STDOUT_COPY = STDOUT_FILENO;
// no race here, since it is called early
util::make_socket_closeonexec(STDERR_COPY);
}
void restore_original_fds() { dup2(STDERR_COPY, STDERR_FILENO); }
void close_log_file(int &fd) {
if (fd != STDERR_COPY && fd != STDOUT_COPY && fd != -1) {
close(fd);
}
fd = -1;
}
int open_log_file(const char *path) {
if (strcmp(path, "/dev/stdout") == 0 ||
strcmp(path, "/proc/self/fd/1") == 0) {
return STDOUT_COPY;
}
if (strcmp(path, "/dev/stderr") == 0 ||
strcmp(path, "/proc/self/fd/2") == 0) {
return STDERR_COPY;
}
#if defined O_CLOEXEC
auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP);
#else // !O_CLOEXEC
auto fd =
open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
// We get race condition if execve is called at the same time.
if (fd != -1) {
util::make_socket_closeonexec(fd);
}
#endif // !O_CLOEXEC
if (fd == -1) {
return -1;
}
return fd;
}
} // namespace shrpx

View File

@@ -173,6 +173,24 @@ void log_chld(pid_t pid, int rstatus, const char *msg);
void redirect_stderr_to_errorlog();
// Makes internal copy of stderr (and possibly stdout in the future),
// which is then used as pointer to /dev/stderr or /proc/self/fd/2
void store_original_fds();
// Restores the original stderr that was stored with copy_original_fds
// Used just before execv
void restore_original_fds();
// Closes |fd| which was returned by open_log_file (see below)
// and sets it to -1. In the case that |fd| points to stdout or
// stderr, or is -1, the descriptor is not closed (but still set to -1).
void close_log_file(int &fd);
// Opens |path| with O_APPEND enabled. If file does not exist, it is
// created first. This function returns file descriptor referring the
// opened file if it succeeds, or -1.
int open_log_file(const char *path);
} // namespace shrpx
#endif // SHRPX_LOG_H

View File

@@ -83,13 +83,16 @@ void RateLimit::regen() {
avail_ += rate_;
}
if (avail_ > 0 && startw_req_) {
if (w_->fd >= 0 && avail_ > 0 && startw_req_) {
ev_io_start(loop_, w_);
handle_tls_pending_read();
}
}
void RateLimit::startw() {
if (w_->fd < 0) {
return;
}
startw_req_ = true;
if (rate_ == 0 || avail_ > 0) {
ev_io_start(loop_, w_);

View File

@@ -105,7 +105,7 @@ ssize_t recv_callback(spdylay_session *session, uint8_t *buf, size_t len,
auto nread = std::min(rb->rleft(), len);
memcpy(buf, rb->pos, nread);
memcpy(buf, rb->pos(), nread);
rb->drain(nread);
rlimit->startw();

View File

@@ -525,6 +525,63 @@ int sct_parse_cb(SSL *ssl, unsigned int ext_type, const unsigned char *in,
} // namespace
#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
namespace {
unsigned int psk_server_cb(SSL *ssl, const char *identity, unsigned char *psk,
unsigned int max_psk_len) {
auto config = get_config();
auto &tlsconf = config->tls;
auto it = tlsconf.psk_secrets.find(StringRef{identity});
if (it == std::end(tlsconf.psk_secrets)) {
return 0;
}
auto &secret = (*it).second;
if (secret.size() > max_psk_len) {
LOG(ERROR) << "The size of PSK secret is " << secret.size()
<< ", but the acceptable maximum size is" << max_psk_len;
return 0;
}
std::copy(std::begin(secret), std::end(secret), psk);
return static_cast<unsigned int>(secret.size());
}
} // namespace
namespace {
unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity_out,
unsigned int max_identity_len, unsigned char *psk,
unsigned int max_psk_len) {
auto config = get_config();
auto &tlsconf = config->tls;
auto &identity = tlsconf.client.psk.identity;
auto &secret = tlsconf.client.psk.secret;
if (identity.empty()) {
return 0;
}
if (identity.size() + 1 > max_identity_len) {
LOG(ERROR) << "The size of PSK identity is " << identity.size()
<< ", but the acceptable maximum size is " << max_identity_len;
return 0;
}
if (secret.size() > max_psk_len) {
LOG(ERROR) << "The size of PSK secret is " << secret.size()
<< ", but the acceptable maximum size is " << max_psk_len;
return 0;
}
*std::copy(std::begin(identity), std::end(identity), identity_out) = '\0';
std::copy(std::begin(secret), std::end(secret), psk);
return (unsigned int)secret.size();
}
} // namespace
struct TLSProtocol {
StringRef name;
long int mask;
@@ -577,6 +634,8 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
const unsigned char sid_ctx[] = "shrpx";
SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1);
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
@@ -588,15 +647,8 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
SSL_CTX_set_timeout(ssl_ctx, tlsconf.session_timeout.count());
const char *ciphers;
if (!tlsconf.ciphers.empty()) {
ciphers = tlsconf.ciphers.c_str();
} else {
ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST;
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) {
LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers
if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.ciphers.c_str()) == 0) {
LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
@@ -720,18 +772,22 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
// SSL_extension_supported(TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP)
// returns 1, which means OpenSSL internally handles it. But
// OpenSSL handles signed_certificate_timestamp extension specially,
// and it lets custom handler to process the extension.
if (!sct_data.empty() &&
SSL_extension_supported(TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP) == 0) {
if (SSL_CTX_add_server_custom_ext(
ssl_ctx, TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP, sct_add_cb,
sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) {
LOG(FATAL) << "SSL_CTX_add_server_custom_ext failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
SSL_CTX_add_server_custom_ext(
ssl_ctx, TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP, sct_add_cb,
sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) {
LOG(FATAL) << "SSL_CTX_add_server_custom_ext failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
SSL_CTX_set_psk_server_callback(ssl_ctx, psk_server_cb);
auto tls_ctx_data = new TLSContextData();
tls_ctx_data->cert_file = cert_file;
tls_ctx_data->sct_data = sct_data;
@@ -812,14 +868,10 @@ SSL_CTX *create_ssl_client_context(
SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
const char *ciphers;
if (!tlsconf.ciphers.empty()) {
ciphers = tlsconf.ciphers.c_str();
} else {
ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST;
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) {
LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.client.ciphers.c_str()) == 0) {
LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.client.ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
@@ -871,6 +923,8 @@ SSL_CTX *create_ssl_client_context(
#endif // HAVE_NEVERBLEED
}
SSL_CTX_set_psk_client_callback(ssl_ctx, psk_client_cb);
// NPN selection callback. This is required to set SSL_CTX because
// OpenSSL does not offer SSL_set_next_proto_select_cb.
SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_cb, nullptr);
@@ -1153,8 +1207,10 @@ int verify_hostname(X509 *cert, const StringRef &hostname,
int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
auto cert = SSL_get_peer_certificate(ssl);
if (!cert) {
LOG(ERROR) << "No certificate found";
return -1;
// By the protocol definition, TLS server always sends certificate
// if it has. If certificate cannot be retrieved, authentication
// without certificate is used, such as PSK.
return 0;
}
auto cert_deleter = defer(X509_free, cert);
auto verify_res = SSL_get_verify_result(ssl);

View File

@@ -38,22 +38,6 @@ namespace nghttp2 {
namespace ssl {
// Recommended general purpose "Intermediate compatibility" cipher
// suites by mozilla.
//
// https://wiki.mozilla.org/Security/Server_Side_TLS
const char *const DEFAULT_CIPHER_LIST =
"ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-"
"AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-"
"SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-"
"AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-"
"ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-"
"AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-"
"SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-"
"ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-"
"SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-"
"SHA:DES-CBC3-SHA:!DSS";
#if OPENSSL_1_1_API
// CRYPTO_LOCK is deprecated as of OpenSSL 1.1.0
@@ -99,6 +83,8 @@ const char *get_tls_protocol(SSL *ssl) {
return "SSLv2";
case SSL3_VERSION:
return "SSLv3";
case TLS1_3_VERSION:
return "TLSv1.3";
case TLS1_2_VERSION:
return "TLSv1.2";
case TLS1_1_VERSION:
@@ -156,7 +142,7 @@ bool check_http2_cipher_black_list(SSL *ssl) {
bool check_http2_tls_version(SSL *ssl) {
auto tls_ver = SSL_version(ssl);
return tls_ver == TLS1_2_VERSION;
return tls_ver >= TLS1_2_VERSION;
}
bool check_http2_requirement(SSL *ssl) {

View File

@@ -45,7 +45,21 @@ public:
LibsslGlobalLock &operator=(const LibsslGlobalLock &) = delete;
};
extern const char *const DEFAULT_CIPHER_LIST;
// Recommended general purpose "Intermediate compatibility" cipher
// suites by mozilla.
//
// https://wiki.mozilla.org/Security/Server_Side_TLS
constexpr char DEFAULT_CIPHER_LIST[] =
"ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-"
"AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-"
"SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-"
"AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-"
"ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-"
"AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-"
"SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-"
"ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-"
"SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-"
"SHA:DES-CBC3-SHA:!DSS";
const char *get_tls_protocol(SSL *ssl);

View File

@@ -417,7 +417,7 @@ public:
explicit StringRef(const char *s) : base(s), len(strlen(s)) {}
constexpr StringRef(const char *s, size_t n) : base(s), len(n) {}
template <typename CharT>
StringRef(const CharT *s, size_t n)
constexpr StringRef(const CharT *s, size_t n)
: base(reinterpret_cast<const char *>(s)), len(n) {}
template <typename InputIt>
StringRef(InputIt first, InputIt last)

View File

@@ -205,9 +205,10 @@ Iterator cpydig(Iterator d, uint32_t n, size_t len) {
} // namespace
namespace {
const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
constexpr const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
constexpr const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"};
} // namespace
std::string http_date(time_t t) {
@@ -369,7 +370,7 @@ char upcase(char c) {
}
namespace {
const char LOWER_XDIGITS[] = "0123456789abcdef";
constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
} // namespace
std::string format_hex(const unsigned char *s, size_t len) {
@@ -472,7 +473,7 @@ int levenshtein(const char *a, int alen, const char *b, int blen, int swapcost,
}
} // namespace
void show_candidates(const char *unkopt, option *options) {
void show_candidates(const char *unkopt, const option *options) {
for (; *unkopt == '-'; ++unkopt)
;
if (*unkopt == '\0') {
@@ -677,60 +678,6 @@ void set_port(Address &addr, uint16_t port) {
}
}
static int STDERR_COPY = -1;
static int STDOUT_COPY = -1;
void store_original_fds() {
// consider dup'ing stdout too
STDERR_COPY = dup(STDERR_FILENO);
STDOUT_COPY = STDOUT_FILENO;
// no race here, since it is called early
make_socket_closeonexec(STDERR_COPY);
}
void restore_original_fds() { dup2(STDERR_COPY, STDERR_FILENO); }
void close_log_file(int &fd) {
if (fd != STDERR_COPY && fd != STDOUT_COPY && fd != -1) {
close(fd);
}
fd = -1;
}
int open_log_file(const char *path) {
if (strcmp(path, "/dev/stdout") == 0 ||
strcmp(path, "/proc/self/fd/1") == 0) {
return STDOUT_COPY;
}
if (strcmp(path, "/dev/stderr") == 0 ||
strcmp(path, "/proc/self/fd/2") == 0) {
return STDERR_COPY;
}
#if defined O_CLOEXEC
auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP);
#else // !O_CLOEXEC
auto fd =
open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
// We get race condition if execve is called at the same time.
if (fd != -1) {
make_socket_closeonexec(fd);
}
#endif // !O_CLOEXEC
if (fd == -1) {
return -1;
}
return fd;
}
std::string ascii_dump(const uint8_t *data, size_t len) {
std::string res;
@@ -1465,6 +1412,30 @@ int sha256(uint8_t *res, const StringRef &s) {
return 0;
}
bool is_hex_string(const StringRef &s) {
if (s.size() % 2) {
return false;
}
for (auto c : s) {
if (!is_hex_digit(c)) {
return false;
}
}
return true;
}
StringRef decode_hex(BlockAllocator &balloc, const StringRef &s) {
auto iov = make_byte_ref(balloc, s.size() + 1);
auto p = iov.base;
for (auto it = std::begin(s); it != std::end(s); it += 2) {
*p++ = (hex_to_uint(*it) << 4) | hex_to_uint(*(it + 1));
}
*p = '\0';
return StringRef{iov.base, p};
}
} // namespace util
} // namespace nghttp2

View File

@@ -82,6 +82,9 @@ inline bool is_hex_digit(const char c) {
return is_digit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
}
// Returns true if |s| is hex string.
bool is_hex_string(const StringRef &s);
bool in_rfc3986_unreserved_chars(const char c);
bool in_rfc3986_sub_delims(const char c);
@@ -147,6 +150,11 @@ template <size_t N> std::string format_hex(const std::array<uint8_t, N> &s) {
StringRef format_hex(BlockAllocator &balloc, const StringRef &s);
// decode_hex decodes hex string |s|, returns the decoded byte string.
// This function assumes |s| is hex string, that is is_hex_string(s)
// == true.
StringRef decode_hex(BlockAllocator &balloc, const StringRef &s);
// Returns given time |t| from epoch in HTTP Date format (e.g., Mon,
// 10 Oct 2016 10:25:58 GMT).
std::string http_date(time_t t);
@@ -176,7 +184,7 @@ time_t parse_http_date(const StringRef &s);
char upcase(char c);
inline char lowcase(char c) {
static unsigned char tbl[] = {
constexpr static unsigned char tbl[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
@@ -443,7 +451,7 @@ void to_token68(std::string &base64str);
StringRef to_base64(BlockAllocator &balloc, const StringRef &token68str);
void show_candidates(const char *unkopt, option *options);
void show_candidates(const char *unkopt, const option *options);
bool has_uri_field(const http_parser_url &u, http_parser_url_fields field);
@@ -481,24 +489,6 @@ std::string to_numeric_addr(const Address *addr);
// Sets |port| to |addr|.
void set_port(Address &addr, uint16_t port);
// Makes internal copy of stderr (and possibly stdout in the future),
// which is then used as pointer to /dev/stderr or /proc/self/fd/2
void store_original_fds();
// Restores the original stderr that was stored with copy_original_fds
// Used just before execv
void restore_original_fds();
// Closes |fd| which was returned by open_log_file (see below)
// and sets it to -1. In the case that |fd| points to stdout or
// stderr, or is -1, the descriptor is not closed (but still set to -1).
void close_log_file(int &fd);
// Opens |path| with O_APPEND enabled. If file does not exist, it is
// created first. This function returns file descriptor referring the
// opened file if it succeeds, or -1.
int open_log_file(const char *path);
// Returns ASCII dump of |data| of length |len|. Only ASCII printable
// characters are preserved. Other characters are replaced with ".".
std::string ascii_dump(const uint8_t *data, size_t len);

View File

@@ -593,4 +593,20 @@ void test_util_format_hex(void) {
CU_ASSERT("" == util::format_hex(balloc, StringRef::from_lit("")));
}
void test_util_is_hex_string(void) {
CU_ASSERT(util::is_hex_string(StringRef{}));
CU_ASSERT(util::is_hex_string(StringRef::from_lit("0123456789abcdef")));
CU_ASSERT(util::is_hex_string(StringRef::from_lit("0123456789ABCDEF")));
CU_ASSERT(!util::is_hex_string(StringRef::from_lit("000")));
CU_ASSERT(!util::is_hex_string(StringRef::from_lit("XX")));
}
void test_util_decode_hex(void) {
BlockAllocator balloc(4096, 4096);
CU_ASSERT("\x0f\xf0" ==
util::decode_hex(balloc, StringRef::from_lit("0ff0")));
CU_ASSERT("" == util::decode_hex(balloc, StringRef{}));
}
} // namespace shrpx

View File

@@ -64,6 +64,8 @@ void test_util_make_hostport(void);
void test_util_strifind(void);
void test_util_random_alpha_digit(void);
void test_util_format_hex(void);
void test_util_is_hex_string(void);
void test_util_decode_hex(void);
} // namespace shrpx