mirror of
https://github.com/nghttp2/nghttp2.git
synced 2026-03-26 16:06:14 +08:00
Compare commits
47 Commits
v1.18.x
...
boringssl-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1beff1e10 | ||
|
|
b1b8308555 | ||
|
|
9b574a5a76 | ||
|
|
0567f1f038 | ||
|
|
4be5de1163 | ||
|
|
9db1c9467c | ||
|
|
3444b42d44 | ||
|
|
6595ae26ea | ||
|
|
41d8a3ac09 | ||
|
|
175001a8d9 | ||
|
|
7cf9e00283 | ||
|
|
8a3eb3f066 | ||
|
|
7e1a0d204b | ||
|
|
cbca2e35b5 | ||
|
|
fc9bdf024f | ||
|
|
3f97e6cd3a | ||
|
|
4fa150c494 | ||
|
|
e8b2508036 | ||
|
|
ac399e41ac | ||
|
|
95dd908834 | ||
|
|
9c7e54d9b5 | ||
|
|
3c03024881 | ||
|
|
36dfc0a56a | ||
|
|
55bf6cdb15 | ||
|
|
0abc220013 | ||
|
|
c28900990a | ||
|
|
5108193d7b | ||
|
|
79a24f5dd9 | ||
|
|
83c759572c | ||
|
|
1a07fb000b | ||
|
|
4aab15999d | ||
|
|
441982674f | ||
|
|
8256c6e070 | ||
|
|
ae87a44b94 | ||
|
|
87d1692e27 | ||
|
|
1d2f008656 | ||
|
|
b064d8a9ff | ||
|
|
528af200b6 | ||
|
|
c6827a7dac | ||
|
|
55ecb082ee | ||
|
|
5f2cf461e6 | ||
|
|
b313386988 | ||
|
|
3933280d29 | ||
|
|
2b6073900f | ||
|
|
d1ba43a69f | ||
|
|
a0779edec4 | ||
|
|
d70fefe72f |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
.
|
||||
|
||||
@@ -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
|
||||
.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
----------------------------------------
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'},
|
||||
|
||||
@@ -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'},
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
87
src/shrpx.cc
87
src/shrpx.cc
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: "
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
110
src/shrpx_ssl.cc
110
src/shrpx_ssl.cc
@@ -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);
|
||||
|
||||
20
src/ssl.cc
20
src/ssl.cc
@@ -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) {
|
||||
|
||||
16
src/ssl.h
16
src/ssl.h
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
89
src/util.cc
89
src/util.cc
@@ -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
|
||||
|
||||
30
src/util.h
30
src/util.h
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user