New upstream version 1.26.0

This commit is contained in:
Jan Mojžíš 2024-04-30 17:41:57 +02:00
parent 8094197df9
commit cd2d231dd0
141 changed files with 27334 additions and 2065 deletions

106
CHANGES
View file

@ -1,7 +1,109 @@
Changes with nginx 1.24.0 11 Apr 2023
Changes with nginx 1.26.0 23 Apr 2024
*) 1.24.x stable branch.
*) 1.26.x stable branch.
Changes with nginx 1.25.5 16 Apr 2024
*) Feature: virtual servers in the stream module.
*) Feature: the ngx_stream_pass_module.
*) Feature: the "deferred", "accept_filter", and "setfib" parameters of
the "listen" directive in the stream module.
*) Feature: cache line size detection for some architectures.
Thanks to Piotr Sikora.
*) Feature: support for Homebrew on Apple Silicon.
Thanks to Piotr Sikora.
*) Bugfix: Windows cross-compilation bugfixes and improvements.
Thanks to Piotr Sikora.
*) Bugfix: unexpected connection closure while using 0-RTT in QUIC.
Thanks to Vladimir Khomutov.
Changes with nginx 1.25.4 14 Feb 2024
*) Security: when using HTTP/3 a segmentation fault might occur in a
worker process while processing a specially crafted QUIC session
(CVE-2024-24989, CVE-2024-24990).
*) Bugfix: connections with pending AIO operations might be closed
prematurely during graceful shutdown of old worker processes.
*) Bugfix: socket leak alerts no longer logged when fast shutdown was
requested after graceful shutdown of old worker processes.
*) Bugfix: a socket descriptor error, a socket leak, or a segmentation
fault in a worker process (for SSL proxying) might occur if AIO was
used in a subrequest.
*) Bugfix: a segmentation fault might occur in a worker process if SSL
proxying was used along with the "image_filter" directive and errors
with code 415 were redirected with the "error_page" directive.
*) Bugfixes and improvements in HTTP/3.
Changes with nginx 1.25.3 24 Oct 2023
*) Change: improved detection of misbehaving clients when using HTTP/2.
*) Feature: startup speedup when using a large number of locations.
Thanks to Yusuke Nojima.
*) Bugfix: a segmentation fault might occur in a worker process when
using HTTP/2 without SSL; the bug had appeared in 1.25.1.
*) Bugfix: the "Status" backend response header line with an empty
reason phrase was handled incorrectly.
*) Bugfix: memory leak during reconfiguration when using the PCRE2
library.
Thanks to ZhenZhong Wu.
*) Bugfixes and improvements in HTTP/3.
Changes with nginx 1.25.2 15 Aug 2023
*) Feature: path MTU discovery when using HTTP/3.
*) Feature: TLS_AES_128_CCM_SHA256 cipher suite support when using
HTTP/3.
*) Change: now nginx uses appname "nginx" when loading OpenSSL
configuration.
*) Change: now nginx does not try to load OpenSSL configuration if the
--with-openssl option was used to built OpenSSL and the OPENSSL_CONF
environment variable is not set.
*) Bugfix: in the $body_bytes_sent variable when using HTTP/3.
*) Bugfix: in HTTP/3.
Changes with nginx 1.25.1 13 Jun 2023
*) Feature: the "http2" directive, which enables HTTP/2 on a per-server
basis; the "http2" parameter of the "listen" directive is now
deprecated.
*) Change: HTTP/2 server push support has been removed.
*) Change: the deprecated "ssl" directive is not supported anymore.
*) Bugfix: in HTTP/3 when using OpenSSL.
Changes with nginx 1.25.0 23 May 2023
*) Feature: experimental HTTP/3 support.
Changes with nginx 1.23.4 28 Mar 2023

View file

@ -1,7 +1,114 @@
Изменения в nginx 1.24.0 11.04.2023
Изменения в nginx 1.26.0 23.04.2024
*) Стабильная ветка 1.24.x.
*) Стабильная ветка 1.26.x.
Изменения в nginx 1.25.5 16.04.2024
*) Добавление: виртуальные сервера в модуле stream.
*) Добавление: модуль ngx_stream_pass_module.
*) Добавление: параметры deferred, accept_filter и setfib директивы
listen в модуле stream.
*) Добавление: определение размера строки кеша процессора для некоторых
архитектур.
Спасибо Piotr Sikora.
*) Добавление: поддержка Homebrew на Apple Silicon.
Спасибо Piotr Sikora.
*) Исправление: улучшения и исправления кросс-компиляции для Windows.
Спасибо Piotr Sikora.
*) Исправление: неожиданное закрытие соединения при использовании 0-RTT
в QUIC.
Спасибо Владимиру Хомутову.
Изменения в nginx 1.25.4 14.02.2024
*) Безопасность: при использовании HTTP/3 в рабочем процессе мог
произойти segmentation fault во время обработки специально созданной
QUIC-сессии (CVE-2024-24989, CVE-2024-24990).
*) Исправление: соединения с незавершенными AIO-операциями могли
закрываться преждевременно во время плавного завершения старых
рабочих процессов.
*) Исправление: теперь nginx не пишет в лог сообщения об утечке сокетов,
если во время плавного завершения старых рабочих процессов было
запрошено быстрое завершение.
*) Исправление: при использовании AIO в подзапросе могла происходить
ошибка на сокете, утечка сокетов, либо segmentation fault в рабочем
процессе (при SSL-проксировании).
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалось SSL-проксирование и директива image_filter, а
ошибки с кодом 415 перенаправлялись с помощью директивы error_page.
*) Исправления и улучшения в HTTP/3.
Изменения в nginx 1.25.3 24.10.2023
*) Изменение: улучшено детектирование некорректного поведения клиентов
при использовании HTTP/2.
*) Добавление: уменьшение времени запуска при использовании большого
количества location'ов.
Спасибо Yusuke Nojima.
*) Исправление: при использовании HTTP/2 без SSL в рабочем процессе мог
произойти segmentation fault; ошибка появилась в 1.25.1.
*) Исправление: строка "Status" в заголовке ответа бэкенда с пустой
поясняющей фразой обрабатывалась некорректно.
*) Исправление: утечки памяти во время переконфигурации при
использовании библиотеки PCRE2.
Спасибо ZhenZhong Wu.
*) Исправления и улучшения в HTTP/3.
Изменения в nginx 1.25.2 15.08.2023
*) Добавление: path MTU discovery при использовании HTTP/3.
*) Добавление: поддержка шифра TLS_AES_128_CCM_SHA256 при использовании
HTTP/3.
*) Изменение: теперь при загрузке конфигурации OpenSSL nginx использует
appname "nginx".
*) Изменение: теперь nginx не пытается загружать конфигурацию OpenSSL,
если для сборки OpenSSL использовался параметр --with-openssl и
переменная окружения OPENSSL_CONF не установлена.
*) Исправление: в переменной $body_bytes_sent при использовании HTTP/3.
*) Исправление: в HTTP/3.
Изменения в nginx 1.25.1 13.06.2023
*) Добавление: директива http2, позволяющая включать HTTP/2 в отдельных
блоках server; параметр http2 директивы listen объявлен устаревшим.
*) Изменение: поддержка HTTP/2 server push упразднена.
*) Изменение: устаревшая директива ssl больше не поддерживается.
*) Исправление: в HTTP/3 при использовании OpenSSL.
Изменения в nginx 1.25.0 23.05.2023
*) Добавление: экспериментальная поддержка HTTP/3.
Изменения в nginx 1.23.4 28.03.2023

View file

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

View file

@ -112,7 +112,7 @@ install: build $NGX_INSTALL_PERL_MODULES
test ! -f '\$(DESTDIR)$NGX_SBIN_PATH' \\
|| mv '\$(DESTDIR)$NGX_SBIN_PATH' \\
'\$(DESTDIR)$NGX_SBIN_PATH.old'
cp $NGX_OBJS/nginx '\$(DESTDIR)$NGX_SBIN_PATH'
cp $NGX_OBJS/nginx$ngx_binext '\$(DESTDIR)$NGX_SBIN_PATH'
test -d '\$(DESTDIR)$NGX_CONF_PREFIX' \\
|| mkdir -p '\$(DESTDIR)$NGX_CONF_PREFIX'

View file

@ -64,6 +64,23 @@ if [ $ngx_found = no ]; then
fi
if [ $ngx_found = no ]; then
# Homebrew on Apple Silicon
ngx_feature="GeoIP library in /opt/homebrew/"
ngx_feature_path="/opt/homebrew/include"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R/opt/homebrew/lib -L/opt/homebrew/lib -lGeoIP"
else
ngx_feature_libs="-L/opt/homebrew/lib -lGeoIP"
fi
. auto/feature
fi
if [ $ngx_found = yes ]; then
CORE_INCS="$CORE_INCS $ngx_feature_path"

View file

@ -46,6 +46,22 @@ if [ $ngx_found = no ]; then
fi
if [ $ngx_found = no ]; then
# Homebrew on Apple Silicon
ngx_feature="Google perftools in /opt/homebrew/"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R/opt/homebrew/lib -L/opt/homebrew/lib -lprofiler"
else
ngx_feature_libs="-L/opt/homebrew/lib -lprofiler"
fi
. auto/feature
fi
if [ $ngx_found = yes ]; then
CORE_LIBS="$CORE_LIBS $ngx_feature_libs"

View file

@ -65,6 +65,23 @@ if [ $ngx_found = no ]; then
fi
if [ $ngx_found = no ]; then
# Homebrew on Apple Silicon
ngx_feature="GD library in /opt/homebrew/"
ngx_feature_path="/opt/homebrew/include"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R/opt/homebrew/lib -L/opt/homebrew/lib -lgd"
else
ngx_feature_libs="-L/opt/homebrew/lib -lgd"
fi
. auto/feature
fi
if [ $ngx_found = yes ]; then
CORE_INCS="$CORE_INCS $ngx_feature_path"

View file

@ -5,12 +5,19 @@
if [ $OPENSSL != NONE ]; then
have=NGX_OPENSSL . auto/have
have=NGX_SSL . auto/have
have=NGX_OPENSSL_NO_CONFIG . auto/have
if [ $USE_OPENSSL_QUIC = YES ]; then
have=NGX_QUIC . auto/have
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
fi
case "$CC" in
cl | bcc32)
have=NGX_OPENSSL . auto/have
have=NGX_SSL . auto/have
CFLAGS="$CFLAGS -DNO_SYS_TYPES_H"
CORE_INCS="$CORE_INCS $OPENSSL/openssl/include"
@ -33,9 +40,6 @@ if [ $OPENSSL != NONE ]; then
;;
*)
have=NGX_OPENSSL . auto/have
have=NGX_SSL . auto/have
CORE_INCS="$CORE_INCS $OPENSSL/.openssl/include"
CORE_DEPS="$CORE_DEPS $OPENSSL/.openssl/include/openssl/ssl.h"
CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libssl.a"
@ -118,11 +122,58 @@ else
. auto/feature
fi
if [ $ngx_found = no ]; then
# Homebrew on Apple Silicon
ngx_feature="OpenSSL library in /opt/homebrew/"
ngx_feature_path="/opt/homebrew/include"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R/opt/homebrew/lib -L/opt/homebrew/lib -lssl -lcrypto"
else
ngx_feature_libs="-L/opt/homebrew/lib -lssl -lcrypto"
fi
ngx_feature_libs="$ngx_feature_libs $NGX_LIBDL $NGX_LIBPTHREAD"
. auto/feature
fi
if [ $ngx_found = yes ]; then
have=NGX_SSL . auto/have
CORE_INCS="$CORE_INCS $ngx_feature_path"
CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
OPENSSL=YES
if [ $USE_OPENSSL_QUIC = YES ]; then
ngx_feature="OpenSSL QUIC support"
ngx_feature_name="NGX_QUIC"
ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
. auto/feature
if [ $ngx_found = no ]; then
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
ngx_feature="OpenSSL QUIC compatibility"
ngx_feature_test="SSL_CTX_add_custom_ext(NULL, 0, 0,
NULL, NULL, NULL, NULL, NULL)"
. auto/feature
fi
if [ $ngx_found = no ]; then
cat << END
$0: error: certain modules require OpenSSL QUIC support.
You can either do not enable the modules, or install the OpenSSL library with
QUIC support into the system, or build the OpenSSL library with QUIC support
statically from the source with nginx by using --with-openssl=<path> option.
END
exit 1
fi
fi
fi
fi

View file

@ -182,6 +182,22 @@ else
. auto/feature
fi
if [ $ngx_found = no ]; then
# Homebrew on Apple Silicon
ngx_feature="PCRE library in /opt/homebrew/"
ngx_feature_path="/opt/homebrew/include"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R/opt/homebrew/lib -L/opt/homebrew/lib -lpcre"
else
ngx_feature_libs="-L/opt/homebrew/lib -lpcre"
fi
. auto/feature
fi
if [ $ngx_found = yes ]; then
CORE_INCS="$CORE_INCS $ngx_feature_path"
CORE_LIBS="$CORE_LIBS $ngx_feature_libs"

View file

@ -6,9 +6,10 @@
echo "creating $NGX_MAKEFILE"
mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \
$NGX_OBJS/src/event/quic \
$NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \
$NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/modules \
$NGX_OBJS/src/http/modules/perl \
$NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/v3 \
$NGX_OBJS/src/http/modules $NGX_OBJS/src/http/modules/perl \
$NGX_OBJS/src/mail \
$NGX_OBJS/src/stream \
$NGX_OBJS/src/misc

View file

@ -102,7 +102,7 @@ if [ $HTTP = YES ]; then
fi
if [ $HTTP_V2 = YES ]; then
if [ $HTTP_V2 = YES -o $HTTP_V3 = YES ]; then
HTTP_SRCS="$HTTP_SRCS $HTTP_HUFF_SRCS"
fi
@ -124,6 +124,7 @@ if [ $HTTP = YES ]; then
# ngx_http_header_filter
# ngx_http_chunked_filter
# ngx_http_v2_filter
# ngx_http_v3_filter
# ngx_http_range_header_filter
# ngx_http_gzip_filter
# ngx_http_postpone_filter
@ -156,6 +157,7 @@ if [ $HTTP = YES ]; then
ngx_http_header_filter_module \
ngx_http_chunked_filter_module \
ngx_http_v2_filter_module \
ngx_http_v3_filter_module \
ngx_http_range_header_filter_module \
ngx_http_gzip_filter_module \
ngx_http_postpone_filter_module \
@ -217,6 +219,17 @@ if [ $HTTP = YES ]; then
. auto/module
fi
if [ $HTTP_V3 = YES ]; then
ngx_module_name=ngx_http_v3_filter_module
ngx_module_incs=
ngx_module_deps=
ngx_module_srcs=src/http/v3/ngx_http_v3_filter_module.c
ngx_module_libs=
ngx_module_link=$HTTP_V3
. auto/module
fi
if :; then
ngx_module_name=ngx_http_range_header_filter_module
ngx_module_incs=
@ -410,7 +423,6 @@ if [ $HTTP = YES ]; then
if [ $HTTP_V2 = YES ]; then
have=NGX_HTTP_V2 . auto/have
have=NGX_HTTP_HEADERS . auto/have
ngx_module_name=ngx_http_v2_module
ngx_module_incs=src/http/v2
@ -426,6 +438,32 @@ if [ $HTTP = YES ]; then
. auto/module
fi
if [ $HTTP_V3 = YES ]; then
USE_OPENSSL_QUIC=YES
HTTP_SSL=YES
have=NGX_HTTP_V3 . auto/have
ngx_module_name=ngx_http_v3_module
ngx_module_incs=src/http/v3
ngx_module_deps="src/http/v3/ngx_http_v3.h \
src/http/v3/ngx_http_v3_encode.h \
src/http/v3/ngx_http_v3_parse.h \
src/http/v3/ngx_http_v3_table.h \
src/http/v3/ngx_http_v3_uni.h"
ngx_module_srcs="src/http/v3/ngx_http_v3.c \
src/http/v3/ngx_http_v3_encode.c \
src/http/v3/ngx_http_v3_parse.c \
src/http/v3/ngx_http_v3_table.c \
src/http/v3/ngx_http_v3_uni.c \
src/http/v3/ngx_http_v3_request.c \
src/http/v3/ngx_http_v3_module.c"
ngx_module_libs=
ngx_module_link=$HTTP_V3
. auto/module
fi
if :; then
ngx_module_name=ngx_http_static_module
ngx_module_incs=
@ -1128,6 +1166,16 @@ if [ $STREAM != NO ]; then
. auto/module
fi
if [ $STREAM_PASS = YES ]; then
ngx_module_name=ngx_stream_pass_module
ngx_module_deps=
ngx_module_srcs=src/stream/ngx_stream_pass_module.c
ngx_module_libs=
ngx_module_link=$STREAM_PASS
. auto/module
fi
if [ $STREAM_SET = YES ]; then
ngx_module_name=ngx_stream_set_module
ngx_module_deps=
@ -1272,6 +1320,63 @@ if [ $USE_OPENSSL = YES ]; then
fi
if [ $USE_OPENSSL_QUIC = YES ]; then
ngx_module_type=CORE
ngx_module_name=ngx_quic_module
ngx_module_incs=
ngx_module_deps="src/event/quic/ngx_event_quic.h \
src/event/quic/ngx_event_quic_transport.h \
src/event/quic/ngx_event_quic_protection.h \
src/event/quic/ngx_event_quic_connection.h \
src/event/quic/ngx_event_quic_frames.h \
src/event/quic/ngx_event_quic_connid.h \
src/event/quic/ngx_event_quic_migration.h \
src/event/quic/ngx_event_quic_streams.h \
src/event/quic/ngx_event_quic_ssl.h \
src/event/quic/ngx_event_quic_tokens.h \
src/event/quic/ngx_event_quic_ack.h \
src/event/quic/ngx_event_quic_output.h \
src/event/quic/ngx_event_quic_socket.h \
src/event/quic/ngx_event_quic_openssl_compat.h"
ngx_module_srcs="src/event/quic/ngx_event_quic.c \
src/event/quic/ngx_event_quic_udp.c \
src/event/quic/ngx_event_quic_transport.c \
src/event/quic/ngx_event_quic_protection.c \
src/event/quic/ngx_event_quic_frames.c \
src/event/quic/ngx_event_quic_connid.c \
src/event/quic/ngx_event_quic_migration.c \
src/event/quic/ngx_event_quic_streams.c \
src/event/quic/ngx_event_quic_ssl.c \
src/event/quic/ngx_event_quic_tokens.c \
src/event/quic/ngx_event_quic_ack.c \
src/event/quic/ngx_event_quic_output.c \
src/event/quic/ngx_event_quic_socket.c \
src/event/quic/ngx_event_quic_openssl_compat.c"
ngx_module_libs=
ngx_module_link=YES
ngx_module_order=
. auto/module
if [ $QUIC_BPF = YES -a $SO_COOKIE_FOUND = YES ]; then
ngx_module_type=CORE
ngx_module_name=ngx_quic_bpf_module
ngx_module_incs=
ngx_module_deps=
ngx_module_srcs="src/event/quic/ngx_event_quic_bpf.c \
src/event/quic/ngx_event_quic_bpf_code.c"
ngx_module_libs=
ngx_module_link=YES
ngx_module_order=
. auto/module
have=NGX_QUIC_BPF . auto/have
fi
fi
if [ $USE_PCRE = YES ]; then
ngx_module_type=CORE
ngx_module_name=ngx_regex_module

View file

@ -45,6 +45,8 @@ USE_THREADS=NO
NGX_FILE_AIO=NO
QUIC_BPF=NO
HTTP=YES
NGX_HTTP_LOG_PATH=
@ -59,6 +61,7 @@ HTTP_CHARSET=YES
HTTP_GZIP=YES
HTTP_SSL=NO
HTTP_V2=NO
HTTP_V3=NO
HTTP_SSI=YES
HTTP_REALIP=NO
HTTP_XSLT=NO
@ -124,6 +127,7 @@ STREAM_GEOIP=NO
STREAM_MAP=YES
STREAM_SPLIT_CLIENTS=YES
STREAM_RETURN=YES
STREAM_PASS=YES
STREAM_SET=YES
STREAM_UPSTREAM_HASH=YES
STREAM_UPSTREAM_LEAST_CONN=YES
@ -149,6 +153,7 @@ PCRE_JIT=NO
PCRE2=YES
USE_OPENSSL=NO
USE_OPENSSL_QUIC=NO
OPENSSL=NONE
USE_ZLIB=NO
@ -166,6 +171,8 @@ USE_GEOIP=NO
NGX_GOOGLE_PERFTOOLS=NO
NGX_CPP_TEST=NO
SO_COOKIE_FOUND=NO
NGX_LIBATOMIC=NO
NGX_CPU_CACHE_LINE=
@ -211,6 +218,8 @@ do
--with-file-aio) NGX_FILE_AIO=YES ;;
--without-quic_bpf_module) QUIC_BPF=NONE ;;
--with-ipv6)
NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG
$0: warning: the \"--with-ipv6\" option is deprecated"
@ -228,6 +237,7 @@ $0: warning: the \"--with-ipv6\" option is deprecated"
--with-http_ssl_module) HTTP_SSL=YES ;;
--with-http_v2_module) HTTP_V2=YES ;;
--with-http_v3_module) HTTP_V3=YES ;;
--with-http_realip_module) HTTP_REALIP=YES ;;
--with-http_addition_module) HTTP_ADDITION=YES ;;
--with-http_xslt_module) HTTP_XSLT=YES ;;
@ -328,6 +338,7 @@ use the \"--with-mail_ssl_module\" option instead"
--without-stream_split_clients_module)
STREAM_SPLIT_CLIENTS=NO ;;
--without-stream_return_module) STREAM_RETURN=NO ;;
--without-stream_pass_module) STREAM_PASS=NO ;;
--without-stream_set_module) STREAM_SET=NO ;;
--without-stream_upstream_hash_module)
STREAM_UPSTREAM_HASH=NO ;;
@ -443,8 +454,11 @@ cat << END
--with-file-aio enable file AIO support
--without-quic_bpf_module disable ngx_quic_bpf_module
--with-http_ssl_module enable ngx_http_ssl_module
--with-http_v2_module enable ngx_http_v2_module
--with-http_v3_module enable ngx_http_v3_module
--with-http_realip_module enable ngx_http_realip_module
--with-http_addition_module enable ngx_http_addition_module
--with-http_xslt_module enable ngx_http_xslt_module
@ -544,6 +558,7 @@ cat << END
--without-stream_split_clients_module
disable ngx_stream_split_clients_module
--without-stream_return_module disable ngx_stream_return_module
--without-stream_pass_module disable ngx_stream_pass_module
--without-stream_set_module disable ngx_stream_set_module
--without-stream_upstream_hash_module
disable ngx_stream_upstream_hash_module

View file

@ -115,6 +115,21 @@ case "$NGX_MACHINE" in
NGX_MACH_CACHE_LINE=64
;;
ppc64* | powerpc64*)
have=NGX_ALIGNMENT value=16 . auto/define
NGX_MACH_CACHE_LINE=128
;;
riscv64)
have=NGX_ALIGNMENT value=16 . auto/define
NGX_MACH_CACHE_LINE=64
;;
s390x)
have=NGX_ALIGNMENT value=16 . auto/define
NGX_MACH_CACHE_LINE=256
;;
*)
have=NGX_ALIGNMENT value=16 . auto/define
NGX_MACH_CACHE_LINE=32

View file

@ -228,10 +228,58 @@ ngx_feature_test="struct crypt_data cd;
crypt_r(\"key\", \"salt\", &cd);"
. auto/feature
if [ $ngx_found = yes ]; then
CRYPT_LIB="-lcrypt"
fi
ngx_include="sys/vfs.h"; . auto/include
# BPF sockhash
ngx_feature="BPF sockhash"
ngx_feature_name="NGX_HAVE_BPF"
ngx_feature_run=no
ngx_feature_incs="#include <linux/bpf.h>
#include <sys/syscall.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="union bpf_attr attr = { 0 };
attr.map_flags = 0;
attr.map_type = BPF_MAP_TYPE_SOCKHASH;
syscall(__NR_bpf, 0, &attr, 0);"
. auto/feature
if [ $ngx_found = yes ]; then
CORE_SRCS="$CORE_SRCS src/core/ngx_bpf.c"
CORE_DEPS="$CORE_DEPS src/core/ngx_bpf.h"
if [ $QUIC_BPF != NONE ]; then
QUIC_BPF=YES
fi
fi
ngx_feature="SO_COOKIE"
ngx_feature_name="NGX_HAVE_SO_COOKIE"
ngx_feature_run=no
ngx_feature_incs="#include <sys/socket.h>
$NGX_INCLUDE_INTTYPES_H"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="socklen_t optlen = sizeof(uint64_t);
uint64_t cookie;
getsockopt(0, SOL_SOCKET, SO_COOKIE, &cookie, &optlen)"
. auto/feature
if [ $ngx_found = yes ]; then
SO_COOKIE_FOUND=YES
fi
# UDP segmentation offloading
ngx_feature="UDP_SEGMENT"

View file

@ -18,7 +18,7 @@ ngx_binext=".exe"
case "$NGX_CC_NAME" in
gcc)
clang | gcc)
CORE_LIBS="$CORE_LIBS -ladvapi32 -lws2_32"
MAIN_LINK="$MAIN_LINK -Wl,--export-all-symbols"
MAIN_LINK="$MAIN_LINK -Wl,--out-implib=$NGX_OBJS/libnginx.a"

View file

@ -83,7 +83,7 @@ CORE_SRCS="src/core/nginx.c \
EVENT_MODULES="ngx_events_module ngx_event_core_module"
EVENT_INCS="src/event src/event/modules"
EVENT_INCS="src/event src/event/modules src/event/quic"
EVENT_DEPS="src/event/ngx_event.h \
src/event/ngx_event_timer.h \

View file

@ -448,6 +448,54 @@ ngx_feature_test="setsockopt(0, IPPROTO_IPV6, IPV6_RECVPKTINFO, NULL, 0)"
. auto/feature
# IP packet fragmentation
ngx_feature="IP_MTU_DISCOVER"
ngx_feature_name="NGX_HAVE_IP_MTU_DISCOVER"
ngx_feature_run=no
ngx_feature_incs="#include <sys/socket.h>
#include <netinet/in.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="(void) IP_PMTUDISC_DO;
setsockopt(0, IPPROTO_IP, IP_MTU_DISCOVER, NULL, 0)"
. auto/feature
ngx_feature="IPV6_MTU_DISCOVER"
ngx_feature_name="NGX_HAVE_IPV6_MTU_DISCOVER"
ngx_feature_run=no
ngx_feature_incs="#include <sys/socket.h>
#include <netinet/in.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="(void) IPV6_PMTUDISC_DO;
setsockopt(0, IPPROTO_IPV6, IPV6_MTU_DISCOVER, NULL, 0)"
. auto/feature
ngx_feature="IP_DONTFRAG"
ngx_feature_name="NGX_HAVE_IP_DONTFRAG"
ngx_feature_run=no
ngx_feature_incs="#include <sys/socket.h>
#include <netinet/in.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="setsockopt(0, IPPROTO_IP, IP_DONTFRAG, NULL, 0)"
. auto/feature
ngx_feature="IPV6_DONTFRAG"
ngx_feature_name="NGX_HAVE_IPV6_DONTFRAG"
ngx_feature_run=no
ngx_feature_incs="#include <sys/socket.h>
#include <netinet/in.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="setsockopt(0, IPPROTO_IP, IPV6_DONTFRAG, NULL, 0)"
. auto/feature
ngx_feature="TCP_DEFER_ACCEPT"
ngx_feature_name="NGX_HAVE_DEFERRED_ACCEPT"
ngx_feature_run=no

View file

@ -65,12 +65,12 @@ syn match ngxListenComment '#.*$'
\ contained
\ nextgroup=@ngxListenParams skipwhite skipempty
syn keyword ngxListenOptions contained
\ default_server ssl http2 proxy_protocol
\ default_server ssl quic proxy_protocol
\ setfib fastopen backlog rcvbuf sndbuf accept_filter deferred bind
\ ipv6only reuseport so_keepalive
\ nextgroup=@ngxListenParams skipwhite skipempty
syn keyword ngxListenOptionsDeprecated contained
\ spdy
\ http2
\ nextgroup=@ngxListenParams skipwhite skipempty
syn cluster ngxListenParams
\ contains=ngxListenParam,ngxListenString,ngxListenComment
@ -90,7 +90,6 @@ syn keyword ngxDirectiveBlock contained if
syn keyword ngxDirectiveBlock contained geo
syn keyword ngxDirectiveBlock contained map
syn keyword ngxDirectiveBlock contained split_clients
syn keyword ngxDirectiveBlock contained match
syn keyword ngxDirectiveImportant contained include
syn keyword ngxDirectiveImportant contained root
@ -113,7 +112,6 @@ syn keyword ngxDirectiveError contained post_action
syn keyword ngxDirectiveDeprecated contained proxy_downstream_buffer
syn keyword ngxDirectiveDeprecated contained proxy_upstream_buffer
syn keyword ngxDirectiveDeprecated contained ssl
syn keyword ngxDirectiveDeprecated contained http2_idle_timeout
syn keyword ngxDirectiveDeprecated contained http2_max_field_size
syn keyword ngxDirectiveDeprecated contained http2_max_header_size
@ -136,7 +134,6 @@ syn keyword ngxDirective contained alias
syn keyword ngxDirective contained allow
syn keyword ngxDirective contained ancient_browser
syn keyword ngxDirective contained ancient_browser_value
syn keyword ngxDirective contained api
syn keyword ngxDirective contained auth_basic
syn keyword ngxDirective contained auth_basic_user_file
syn keyword ngxDirective contained auth_delay
@ -144,15 +141,6 @@ syn keyword ngxDirective contained auth_http
syn keyword ngxDirective contained auth_http_header
syn keyword ngxDirective contained auth_http_pass_client_cert
syn keyword ngxDirective contained auth_http_timeout
syn keyword ngxDirective contained auth_jwt
syn keyword ngxDirective contained auth_jwt_claim_set
syn keyword ngxDirective contained auth_jwt_header_set
syn keyword ngxDirective contained auth_jwt_key_cache
syn keyword ngxDirective contained auth_jwt_key_file
syn keyword ngxDirective contained auth_jwt_key_request
syn keyword ngxDirective contained auth_jwt_leeway
syn keyword ngxDirective contained auth_jwt_require
syn keyword ngxDirective contained auth_jwt_type
syn keyword ngxDirective contained auth_request
syn keyword ngxDirective contained auth_request_set
syn keyword ngxDirective contained autoindex
@ -193,8 +181,6 @@ syn keyword ngxDirective contained error_log
syn keyword ngxDirective contained etag
syn keyword ngxDirective contained eventport_events
syn keyword ngxDirective contained expires
syn keyword ngxDirective contained f4f
syn keyword ngxDirective contained f4f_buffer_size
syn keyword ngxDirective contained fastcgi_bind
syn keyword ngxDirective contained fastcgi_buffer_size
syn keyword ngxDirective contained fastcgi_buffering
@ -211,7 +197,6 @@ syn keyword ngxDirective contained fastcgi_cache_max_range_offset
syn keyword ngxDirective contained fastcgi_cache_methods
syn keyword ngxDirective contained fastcgi_cache_min_uses
syn keyword ngxDirective contained fastcgi_cache_path
syn keyword ngxDirective contained fastcgi_cache_purge
syn keyword ngxDirective contained fastcgi_cache_revalidate
syn keyword ngxDirective contained fastcgi_cache_use_stale
syn keyword ngxDirective contained fastcgi_cache_valid
@ -295,14 +280,7 @@ syn keyword ngxDirective contained gzip_types
syn keyword ngxDirective contained gzip_vary
syn keyword ngxDirective contained gzip_window
syn keyword ngxDirective contained hash
syn keyword ngxDirective contained health_check
syn keyword ngxDirective contained health_check_timeout
syn keyword ngxDirective contained hls
syn keyword ngxDirective contained hls_buffers
syn keyword ngxDirective contained hls_forward_args
syn keyword ngxDirective contained hls_fragment
syn keyword ngxDirective contained hls_mp4_buffer_size
syn keyword ngxDirective contained hls_mp4_max_buffer_size
syn keyword ngxDirective contained http2
syn keyword ngxDirective contained http2_body_preread_size
syn keyword ngxDirective contained http2_chunk_size
syn keyword ngxDirective contained http2_max_concurrent_pushes
@ -312,6 +290,10 @@ syn keyword ngxDirective contained http2_push
syn keyword ngxDirective contained http2_push_preload
syn keyword ngxDirective contained http2_recv_buffer_size
syn keyword ngxDirective contained http2_streams_index_size
syn keyword ngxDirective contained http3
syn keyword ngxDirective contained http3_hq
syn keyword ngxDirective contained http3_max_concurrent_streams
syn keyword ngxDirective contained http3_stream_buffer_size
syn keyword ngxDirective contained if_modified_since
syn keyword ngxDirective contained ignore_invalid_headers
syn keyword ngxDirective contained image_filter
@ -342,21 +324,20 @@ syn keyword ngxDirective contained js_filter
syn keyword ngxDirective contained js_header_filter
syn keyword ngxDirective contained js_import
syn keyword ngxDirective contained js_path
syn keyword ngxDirective contained js_preload_object
syn keyword ngxDirective contained js_preread
syn keyword ngxDirective contained js_set
syn keyword ngxDirective contained js_shared_dict_zone
syn keyword ngxDirective contained js_var
syn keyword ngxDirective contained keepalive
syn keyword ngxDirective contained keepalive_disable
syn keyword ngxDirective contained keepalive_requests
syn keyword ngxDirective contained keepalive_time
syn keyword ngxDirective contained keepalive_timeout
syn keyword ngxDirective contained keyval
syn keyword ngxDirective contained keyval_zone
syn keyword ngxDirective contained kqueue_changes
syn keyword ngxDirective contained kqueue_events
syn keyword ngxDirective contained large_client_header_buffers
syn keyword ngxDirective contained least_conn
syn keyword ngxDirective contained least_time
syn keyword ngxDirective contained limit_conn
syn keyword ngxDirective contained limit_conn_dry_run
syn keyword ngxDirective contained limit_conn_log_level
@ -400,14 +381,11 @@ syn keyword ngxDirective contained modern_browser
syn keyword ngxDirective contained modern_browser_value
syn keyword ngxDirective contained mp4
syn keyword ngxDirective contained mp4_buffer_size
syn keyword ngxDirective contained mp4_limit_rate
syn keyword ngxDirective contained mp4_limit_rate_after
syn keyword ngxDirective contained mp4_max_buffer_size
syn keyword ngxDirective contained mp4_start_key_frame
syn keyword ngxDirective contained msie_padding
syn keyword ngxDirective contained msie_refresh
syn keyword ngxDirective contained multi_accept
syn keyword ngxDirective contained ntlm
syn keyword ngxDirective contained open_file_cache
syn keyword ngxDirective contained open_file_cache_errors
syn keyword ngxDirective contained open_file_cache_events
@ -450,7 +428,6 @@ syn keyword ngxDirective contained proxy_cache_max_range_offset
syn keyword ngxDirective contained proxy_cache_methods
syn keyword ngxDirective contained proxy_cache_min_uses
syn keyword ngxDirective contained proxy_cache_path
syn keyword ngxDirective contained proxy_cache_purge
syn keyword ngxDirective contained proxy_cache_revalidate
syn keyword ngxDirective contained proxy_cache_use_stale
syn keyword ngxDirective contained proxy_cache_valid
@ -488,7 +465,6 @@ syn keyword ngxDirective contained proxy_requests
syn keyword ngxDirective contained proxy_responses
syn keyword ngxDirective contained proxy_send_lowat
syn keyword ngxDirective contained proxy_send_timeout
syn keyword ngxDirective contained proxy_session_drop
syn keyword ngxDirective contained proxy_set_body
syn keyword ngxDirective contained proxy_set_header
syn keyword ngxDirective contained proxy_smtp_auth
@ -513,7 +489,11 @@ syn keyword ngxDirective contained proxy_temp_file_write_size
syn keyword ngxDirective contained proxy_temp_path
syn keyword ngxDirective contained proxy_timeout
syn keyword ngxDirective contained proxy_upload_rate
syn keyword ngxDirective contained queue
syn keyword ngxDirective contained quic_active_connection_id_limit
syn keyword ngxDirective contained quic_bpf
syn keyword ngxDirective contained quic_gso
syn keyword ngxDirective contained quic_host_key
syn keyword ngxDirective contained quic_retry
syn keyword ngxDirective contained random
syn keyword ngxDirective contained random_index
syn keyword ngxDirective contained read_ahead
@ -544,7 +524,6 @@ syn keyword ngxDirective contained scgi_cache_max_range_offset
syn keyword ngxDirective contained scgi_cache_methods
syn keyword ngxDirective contained scgi_cache_min_uses
syn keyword ngxDirective contained scgi_cache_path
syn keyword ngxDirective contained scgi_cache_purge
syn keyword ngxDirective contained scgi_cache_revalidate
syn keyword ngxDirective contained scgi_cache_use_stale
syn keyword ngxDirective contained scgi_cache_valid
@ -583,9 +562,6 @@ syn keyword ngxDirective contained server_name_in_redirect
syn keyword ngxDirective contained server_names_hash_bucket_size
syn keyword ngxDirective contained server_names_hash_max_size
syn keyword ngxDirective contained server_tokens
syn keyword ngxDirective contained session_log
syn keyword ngxDirective contained session_log_format
syn keyword ngxDirective contained session_log_zone
syn keyword ngxDirective contained set_real_ip_from
syn keyword ngxDirective contained slice
syn keyword ngxDirective contained smtp_auth
@ -633,11 +609,6 @@ syn keyword ngxDirective contained ssl_trusted_certificate
syn keyword ngxDirective contained ssl_verify_client
syn keyword ngxDirective contained ssl_verify_depth
syn keyword ngxDirective contained starttls
syn keyword ngxDirective contained state
syn keyword ngxDirective contained status
syn keyword ngxDirective contained status_format
syn keyword ngxDirective contained status_zone
syn keyword ngxDirective contained sticky
syn keyword ngxDirective contained stub_status
syn keyword ngxDirective contained sub_filter
syn keyword ngxDirective contained sub_filter_last_modified
@ -680,7 +651,6 @@ syn keyword ngxDirective contained uwsgi_cache_max_range_offset
syn keyword ngxDirective contained uwsgi_cache_methods
syn keyword ngxDirective contained uwsgi_cache_min_uses
syn keyword ngxDirective contained uwsgi_cache_path
syn keyword ngxDirective contained uwsgi_cache_purge
syn keyword ngxDirective contained uwsgi_cache_revalidate
syn keyword ngxDirective contained uwsgi_cache_use_stale
syn keyword ngxDirective contained uwsgi_cache_valid
@ -744,6 +714,62 @@ syn keyword ngxDirective contained xslt_string_param
syn keyword ngxDirective contained xslt_stylesheet
syn keyword ngxDirective contained xslt_types
syn keyword ngxDirective contained zone
" nginx-plus commercial extensions directives
syn keyword ngxDirectiveBlock contained match
syn keyword ngxDirectiveBlock contained otel_exporter
syn keyword ngxDirective contained api
syn keyword ngxDirective contained auth_jwt
syn keyword ngxDirective contained auth_jwt_claim_set
syn keyword ngxDirective contained auth_jwt_header_set
syn keyword ngxDirective contained auth_jwt_key_cache
syn keyword ngxDirective contained auth_jwt_key_file
syn keyword ngxDirective contained auth_jwt_key_request
syn keyword ngxDirective contained auth_jwt_leeway
syn keyword ngxDirective contained auth_jwt_require
syn keyword ngxDirective contained auth_jwt_type
syn keyword ngxDirective contained f4f
syn keyword ngxDirective contained f4f_buffer_size
syn keyword ngxDirective contained fastcgi_cache_purge
syn keyword ngxDirective contained health_check
syn keyword ngxDirective contained health_check_timeout
syn keyword ngxDirective contained hls
syn keyword ngxDirective contained hls_buffers
syn keyword ngxDirective contained hls_forward_args
syn keyword ngxDirective contained hls_fragment
syn keyword ngxDirective contained hls_mp4_buffer_size
syn keyword ngxDirective contained hls_mp4_max_buffer_size
syn keyword ngxDirective contained internal_redirect
syn keyword ngxDirective contained keyval
syn keyword ngxDirective contained keyval_zone
syn keyword ngxDirective contained least_time
syn keyword ngxDirective contained mp4_limit_rate
syn keyword ngxDirective contained mp4_limit_rate_after
syn keyword ngxDirective contained mqtt
syn keyword ngxDirective contained mqtt_preread
syn keyword ngxDirective contained mqtt_rewrite_buffer_size
syn keyword ngxDirective contained mqtt_set_connect
syn keyword ngxDirective contained ntlm
syn keyword ngxDirective contained otel_service_name
syn keyword ngxDirective contained otel_span_attr
syn keyword ngxDirective contained otel_span_name
syn keyword ngxDirective contained otel_trace
syn keyword ngxDirective contained otel_trace_context
syn keyword ngxDirective contained proxy_cache_purge
syn keyword ngxDirective contained proxy_session_drop
syn keyword ngxDirective contained queue
syn keyword ngxDirective contained scgi_cache_purge
syn keyword ngxDirective contained session_log
syn keyword ngxDirective contained session_log_format
syn keyword ngxDirective contained session_log_zone
syn keyword ngxDirective contained state
syn keyword ngxDirective contained status
syn keyword ngxDirective contained status_format
syn keyword ngxDirective contained status_zone
syn keyword ngxDirective contained sticky
syn keyword ngxDirective contained uwsgi_cache_purge
syn keyword ngxDirective contained zone_sync
syn keyword ngxDirective contained zone_sync_buffers
syn keyword ngxDirective contained zone_sync_connect_retry_interval
@ -766,7 +792,6 @@ syn keyword ngxDirective contained zone_sync_ssl_verify
syn keyword ngxDirective contained zone_sync_ssl_verify_depth
syn keyword ngxDirective contained zone_sync_timeout
" 3rd party modules list taken from
" https://github.com/freebsd/freebsd-ports/blob/main/www/nginx-devel/Makefile.extmod
" ----------------------------------------------------------------------------------
@ -837,52 +862,6 @@ syn keyword ngxDirectiveThirdParty contained brotli_window
" https://github.com/torden/ngx_cache_purge
syn keyword ngxDirectiveThirdParty contained cache_purge_response_type
" https://github.com/nginx-clojure/nginx-clojure
syn keyword ngxDirectiveThirdParty contained access_handler_code
syn keyword ngxDirectiveThirdParty contained access_handler_name
syn keyword ngxDirectiveThirdParty contained access_handler_property
syn keyword ngxDirectiveThirdParty contained access_handler_type
syn keyword ngxDirectiveThirdParty contained always_read_body
syn keyword ngxDirectiveThirdParty contained auto_upgrade_ws
syn keyword ngxDirectiveThirdParty contained body_filter_code
syn keyword ngxDirectiveThirdParty contained body_filter_name
syn keyword ngxDirectiveThirdParty contained body_filter_property
syn keyword ngxDirectiveThirdParty contained body_filter_type
syn keyword ngxDirectiveThirdParty contained content_handler_code
syn keyword ngxDirectiveThirdParty contained content_handler_name
syn keyword ngxDirectiveThirdParty contained content_handler_property
syn keyword ngxDirectiveThirdParty contained content_handler_type
syn keyword ngxDirectiveThirdParty contained handler_code
syn keyword ngxDirectiveThirdParty contained handler_name
syn keyword ngxDirectiveThirdParty contained handler_type
syn keyword ngxDirectiveThirdParty contained handlers_lazy_init
syn keyword ngxDirectiveThirdParty contained header_filter_code
syn keyword ngxDirectiveThirdParty contained header_filter_name
syn keyword ngxDirectiveThirdParty contained header_filter_property
syn keyword ngxDirectiveThirdParty contained header_filter_type
syn keyword ngxDirectiveThirdParty contained jvm_classpath
syn keyword ngxDirectiveThirdParty contained jvm_classpath_check
syn keyword ngxDirectiveThirdParty contained jvm_exit_handler_code
syn keyword ngxDirectiveThirdParty contained jvm_exit_handler_name
syn keyword ngxDirectiveThirdParty contained jvm_handler_type
syn keyword ngxDirectiveThirdParty contained jvm_init_handler_code
syn keyword ngxDirectiveThirdParty contained jvm_init_handler_name
syn keyword ngxDirectiveThirdParty contained jvm_options
syn keyword ngxDirectiveThirdParty contained jvm_path
syn keyword ngxDirectiveThirdParty contained jvm_var
syn keyword ngxDirectiveThirdParty contained jvm_workers
syn keyword ngxDirectiveThirdParty contained log_handler_code
syn keyword ngxDirectiveThirdParty contained log_handler_name
syn keyword ngxDirectiveThirdParty contained log_handler_property
syn keyword ngxDirectiveThirdParty contained log_handler_type
syn keyword ngxDirectiveThirdParty contained max_balanced_tcp_connections
syn keyword ngxDirectiveThirdParty contained rewrite_handler_code
syn keyword ngxDirectiveThirdParty contained rewrite_handler_name
syn keyword ngxDirectiveThirdParty contained rewrite_handler_property
syn keyword ngxDirectiveThirdParty contained rewrite_handler_type
syn keyword ngxDirectiveThirdParty contained shared_map
syn keyword ngxDirectiveThirdParty contained write_page_size
" https://github.com/AirisX/nginx_cookie_flag_module
syn keyword ngxDirectiveThirdParty contained set_cookie_flag
@ -932,29 +911,6 @@ syn keyword ngxDirectiveThirdParty contained dns_update
syn keyword ngxDirectiveThirdParty contained dynamic_state_file
syn keyword ngxDirectiveThirdParty contained dynamic_upstream
" https://github.com/ZigzagAK/ngx_dynamic_healthcheck
syn keyword ngxDirectiveThirdParty contained check
syn keyword ngxDirectiveThirdParty contained check_disable_host
syn keyword ngxDirectiveThirdParty contained check_exclude_host
syn keyword ngxDirectiveThirdParty contained check_persistent
syn keyword ngxDirectiveThirdParty contained check_request_body
syn keyword ngxDirectiveThirdParty contained check_request_headers
syn keyword ngxDirectiveThirdParty contained check_request_uri
syn keyword ngxDirectiveThirdParty contained check_response_body
syn keyword ngxDirectiveThirdParty contained check_response_codes
syn keyword ngxDirectiveThirdParty contained healthcheck
syn keyword ngxDirectiveThirdParty contained healthcheck_buffer_size
syn keyword ngxDirectiveThirdParty contained healthcheck_disable_host
syn keyword ngxDirectiveThirdParty contained healthcheck_get
syn keyword ngxDirectiveThirdParty contained healthcheck_persistent
syn keyword ngxDirectiveThirdParty contained healthcheck_request_body
syn keyword ngxDirectiveThirdParty contained healthcheck_request_headers
syn keyword ngxDirectiveThirdParty contained healthcheck_request_uri
syn keyword ngxDirectiveThirdParty contained healthcheck_response_body
syn keyword ngxDirectiveThirdParty contained healthcheck_response_codes
syn keyword ngxDirectiveThirdParty contained healthcheck_status
syn keyword ngxDirectiveThirdParty contained healthcheck_update
" https://github.com/openresty/encrypted-session-nginx-module
syn keyword ngxDirectiveThirdParty contained encrypted_session_expires
syn keyword ngxDirectiveThirdParty contained encrypted_session_iv
@ -1004,6 +960,7 @@ syn keyword ngxDirectiveThirdParty contained auth_gss_map_to_local
syn keyword ngxDirectiveThirdParty contained auth_gss_realm
syn keyword ngxDirectiveThirdParty contained auth_gss_service_ccache
syn keyword ngxDirectiveThirdParty contained auth_gss_service_name
syn keyword ngxDirectiveThirdParty contained auth_gss_zone_name
" https://github.com/kvspb/nginx-auth-ldap
syn keyword ngxDirectiveThirdParty contained auth_ldap
@ -1033,6 +990,7 @@ syn keyword ngxDirectiveThirdParty contained eval_subrequest_in_memory
" https://github.com/aperezdc/ngx-fancyindex
syn keyword ngxDirectiveThirdParty contained fancyindex
syn keyword ngxDirectiveThirdParty contained fancyindex_case_sensitive
syn keyword ngxDirectiveThirdParty contained fancyindex_css_href
syn keyword ngxDirectiveThirdParty contained fancyindex_default_sort
syn keyword ngxDirectiveThirdParty contained fancyindex_directories_first
@ -1121,6 +1079,7 @@ syn keyword ngxDirectiveThirdParty contained nchan_publisher_upstream_request
syn keyword ngxDirectiveThirdParty contained nchan_pubsub
syn keyword ngxDirectiveThirdParty contained nchan_pubsub_channel_id
syn keyword ngxDirectiveThirdParty contained nchan_pubsub_location
syn keyword ngxDirectiveThirdParty contained nchan_redis_accurate_subscriber_count
syn keyword ngxDirectiveThirdParty contained nchan_redis_cluster_check_interval
syn keyword ngxDirectiveThirdParty contained nchan_redis_cluster_check_interval_backoff
syn keyword ngxDirectiveThirdParty contained nchan_redis_cluster_check_interval_jitter
@ -1138,6 +1097,11 @@ syn keyword ngxDirectiveThirdParty contained nchan_redis_connect_timeout
syn keyword ngxDirectiveThirdParty contained nchan_redis_discovered_ip_range_blacklist
syn keyword ngxDirectiveThirdParty contained nchan_redis_fakesub_timer_interval
syn keyword ngxDirectiveThirdParty contained nchan_redis_idle_channel_cache_timeout
syn keyword ngxDirectiveThirdParty contained nchan_redis_idle_channel_keepalive_backoff
syn keyword ngxDirectiveThirdParty contained nchan_redis_idle_channel_keepalive_jitter
syn keyword ngxDirectiveThirdParty contained nchan_redis_idle_channel_keepalive_max
syn keyword ngxDirectiveThirdParty contained nchan_redis_idle_channel_keepalive_min
syn keyword ngxDirectiveThirdParty contained nchan_redis_idle_channel_keepalive_safety_margin
syn keyword ngxDirectiveThirdParty contained nchan_redis_load_scripts_unconditionally
syn keyword ngxDirectiveThirdParty contained nchan_redis_namespace
syn keyword ngxDirectiveThirdParty contained nchan_redis_node_connect_timeout
@ -1173,6 +1137,9 @@ syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_server_name
syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_trusted_certificate
syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_trusted_certificate_path
syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_verify_certificate
syn keyword ngxDirectiveThirdParty contained nchan_redis_upstream_stats
syn keyword ngxDirectiveThirdParty contained nchan_redis_upstream_stats_disconnected_timeout
syn keyword ngxDirectiveThirdParty contained nchan_redis_upstream_stats_enabled
syn keyword ngxDirectiveThirdParty contained nchan_redis_url
syn keyword ngxDirectiveThirdParty contained nchan_redis_username
syn keyword ngxDirectiveThirdParty contained nchan_redis_wait_after_connecting
@ -1323,6 +1290,7 @@ syn keyword ngxDirectiveThirdParty contained upload_progress_jsonp_parameter
syn keyword ngxDirectiveThirdParty contained upload_progress_template
" https://github.com/yaoweibin/nginx_upstream_check_module
syn keyword ngxDirectiveThirdParty contained check
syn keyword ngxDirectiveThirdParty contained check_fastcgi_param
syn keyword ngxDirectiveThirdParty contained check_http_expect_alive
syn keyword ngxDirectiveThirdParty contained check_http_send
@ -1335,6 +1303,7 @@ syn keyword ngxDirectiveThirdParty contained fair
syn keyword ngxDirectiveThirdParty contained upstream_fair_shm_size
" https://github.com/ayty-adrianomartins/nginx-sticky-module-ng
syn keyword ngxDirectiveThirdParty contained sticky_hide_cookie
syn keyword ngxDirectiveThirdParty contained sticky_no_fallback
" https://github.com/Novetta/nginx-video-thumbextractor-module
@ -1421,6 +1390,8 @@ syn keyword ngxDirectiveThirdParty contained lua_socket_pool_size
syn keyword ngxDirectiveThirdParty contained lua_socket_read_timeout
syn keyword ngxDirectiveThirdParty contained lua_socket_send_lowat
syn keyword ngxDirectiveThirdParty contained lua_socket_send_timeout
syn keyword ngxDirectiveThirdParty contained lua_ssl_certificate
syn keyword ngxDirectiveThirdParty contained lua_ssl_certificate_key
syn keyword ngxDirectiveThirdParty contained lua_ssl_ciphers
syn keyword ngxDirectiveThirdParty contained lua_ssl_conf_command
syn keyword ngxDirectiveThirdParty contained lua_ssl_crl
@ -1834,16 +1805,6 @@ syn keyword ngxDirectiveThirdParty contained slowfs_cache_purge
syn keyword ngxDirectiveThirdParty contained slowfs_cache_valid
syn keyword ngxDirectiveThirdParty contained slowfs_temp_path
" https://github.com/kawakibi/ngx_small_light
syn keyword ngxDirectiveThirdParty contained small_light
syn keyword ngxDirectiveThirdParty contained small_light_buffer
syn keyword ngxDirectiveThirdParty contained small_light_getparam_mode
syn keyword ngxDirectiveThirdParty contained small_light_imlib2_temp_dir
syn keyword ngxDirectiveThirdParty contained small_light_material_dir
syn keyword ngxDirectiveThirdParty contained small_light_pattern_define
syn keyword ngxDirectiveThirdParty contained small_light_radius_max
syn keyword ngxDirectiveThirdParty contained small_light_sigma_max
" https://github.com/openresty/srcache-nginx-module
syn keyword ngxDirectiveThirdParty contained srcache_buffer
syn keyword ngxDirectiveThirdParty contained srcache_default_expire
@ -1980,6 +1941,14 @@ syn keyword ngxDirectiveThirdParty contained websockify_pass
syn keyword ngxDirectiveThirdParty contained websockify_read_timeout
syn keyword ngxDirectiveThirdParty contained websockify_send_timeout
" https://github.com/vozlt/nginx-module-sts
syn keyword ngxDirectiveThirdParty contained stream_server_traffic_status
syn keyword ngxDirectiveThirdParty contained stream_server_traffic_status_average_method
syn keyword ngxDirectiveThirdParty contained stream_server_traffic_status_display
syn keyword ngxDirectiveThirdParty contained stream_server_traffic_status_display_format
syn keyword ngxDirectiveThirdParty contained stream_server_traffic_status_display_jsonp
syn keyword ngxDirectiveThirdParty contained stream_server_traffic_status_zone
" highlight
hi def link ngxComment Comment

View file

@ -13,6 +13,7 @@
static void ngx_show_version_info(void);
static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle);
static void ngx_cleanup_environment(void *data);
static void ngx_cleanup_environment_variable(void *data);
static ngx_int_t ngx_get_options(int argc, char *const *argv);
static ngx_int_t ngx_process_options(ngx_cycle_t *cycle);
static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv);
@ -518,7 +519,8 @@ ngx_add_inherited_sockets(ngx_cycle_t *cycle)
char **
ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last)
{
char **p, **env;
char **p, **env, *str;
size_t len;
ngx_str_t *var;
ngx_uint_t i, n;
ngx_core_conf_t *ccf;
@ -600,7 +602,31 @@ tz_found:
for (i = 0; i < ccf->env.nelts; i++) {
if (var[i].data[var[i].len] == '=') {
env[n++] = (char *) var[i].data;
if (last) {
env[n++] = (char *) var[i].data;
continue;
}
cln = ngx_pool_cleanup_add(cycle->pool, 0);
if (cln == NULL) {
return NULL;
}
len = ngx_strlen(var[i].data) + 1;
str = ngx_alloc(len, cycle->log);
if (str == NULL) {
return NULL;
}
ngx_memcpy(str, var[i].data, len);
cln->handler = ngx_cleanup_environment_variable;
cln->data = str;
env[n++] = str;
continue;
}
@ -645,6 +671,29 @@ ngx_cleanup_environment(void *data)
}
static void
ngx_cleanup_environment_variable(void *data)
{
char *var = data;
char **p;
for (p = environ; *p; p++) {
/*
* if an environment variable is still used, as it happens on exit,
* the only option is to leak it
*/
if (*p == var) {
return;
}
}
ngx_free(var);
}
ngx_pid_t
ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
{
@ -680,6 +729,9 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
if (ls[i].ignore) {
continue;
}
p = ngx_sprintf(p, "%ud;", ls[i].fd);
}

View file

@ -9,8 +9,8 @@
#define _NGINX_H_INCLUDED_
#define nginx_version 1024000
#define NGINX_VERSION "1.24.0"
#define nginx_version 1026000
#define NGINX_VERSION "1.26.0"
#define NGINX_VER "nginx/" NGINX_VERSION
#ifdef NGX_BUILD

143
src/core/ngx_bpf.c Normal file
View file

@ -0,0 +1,143 @@
/*
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#define NGX_BPF_LOGBUF_SIZE (16 * 1024)
static ngx_inline int
ngx_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size)
{
return syscall(__NR_bpf, cmd, attr, size);
}
void
ngx_bpf_program_link(ngx_bpf_program_t *program, const char *symbol, int fd)
{
ngx_uint_t i;
ngx_bpf_reloc_t *rl;
rl = program->relocs;
for (i = 0; i < program->nrelocs; i++) {
if (ngx_strcmp(rl[i].name, symbol) == 0) {
program->ins[rl[i].offset].src_reg = 1;
program->ins[rl[i].offset].imm = fd;
}
}
}
int
ngx_bpf_load_program(ngx_log_t *log, ngx_bpf_program_t *program)
{
int fd;
union bpf_attr attr;
#if (NGX_DEBUG)
char buf[NGX_BPF_LOGBUF_SIZE];
#endif
ngx_memzero(&attr, sizeof(union bpf_attr));
attr.license = (uintptr_t) program->license;
attr.prog_type = program->type;
attr.insns = (uintptr_t) program->ins;
attr.insn_cnt = program->nins;
#if (NGX_DEBUG)
/* for verifier errors */
attr.log_buf = (uintptr_t) buf;
attr.log_size = NGX_BPF_LOGBUF_SIZE;
attr.log_level = 1;
#endif
fd = ngx_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
if (fd < 0) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
"failed to load BPF program");
ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
"bpf verifier: %s", buf);
return -1;
}
return fd;
}
int
ngx_bpf_map_create(ngx_log_t *log, enum bpf_map_type type, int key_size,
int value_size, int max_entries, uint32_t map_flags)
{
int fd;
union bpf_attr attr;
ngx_memzero(&attr, sizeof(union bpf_attr));
attr.map_type = type;
attr.key_size = key_size;
attr.value_size = value_size;
attr.max_entries = max_entries;
attr.map_flags = map_flags;
fd = ngx_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
if (fd < 0) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
"failed to create BPF map");
return NGX_ERROR;
}
return fd;
}
int
ngx_bpf_map_update(int fd, const void *key, const void *value, uint64_t flags)
{
union bpf_attr attr;
ngx_memzero(&attr, sizeof(union bpf_attr));
attr.map_fd = fd;
attr.key = (uintptr_t) key;
attr.value = (uintptr_t) value;
attr.flags = flags;
return ngx_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
int
ngx_bpf_map_delete(int fd, const void *key)
{
union bpf_attr attr;
ngx_memzero(&attr, sizeof(union bpf_attr));
attr.map_fd = fd;
attr.key = (uintptr_t) key;
return ngx_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
}
int
ngx_bpf_map_lookup(int fd, const void *key, void *value)
{
union bpf_attr attr;
ngx_memzero(&attr, sizeof(union bpf_attr));
attr.map_fd = fd;
attr.key = (uintptr_t) key;
attr.value = (uintptr_t) value;
return ngx_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}

43
src/core/ngx_bpf.h Normal file
View file

@ -0,0 +1,43 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_BPF_H_INCLUDED_
#define _NGX_BPF_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <linux/bpf.h>
typedef struct {
char *name;
int offset;
} ngx_bpf_reloc_t;
typedef struct {
char *license;
enum bpf_prog_type type;
struct bpf_insn *ins;
size_t nins;
ngx_bpf_reloc_t *relocs;
size_t nrelocs;
} ngx_bpf_program_t;
void ngx_bpf_program_link(ngx_bpf_program_t *program, const char *symbol,
int fd);
int ngx_bpf_load_program(ngx_log_t *log, ngx_bpf_program_t *program);
int ngx_bpf_map_create(ngx_log_t *log, enum bpf_map_type type, int key_size,
int value_size, int max_entries, uint32_t map_flags);
int ngx_bpf_map_update(int fd, const void *key, const void *value,
uint64_t flags);
int ngx_bpf_map_delete(int fd, const void *key);
int ngx_bpf_map_lookup(int fd, const void *key, void *value);
#endif /* _NGX_BPF_H_INCLUDED_ */

View file

@ -1013,6 +1013,78 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
}
}
#endif
#if (NGX_HAVE_IP_MTU_DISCOVER)
if (ls[i].quic && ls[i].sockaddr->sa_family == AF_INET) {
value = IP_PMTUDISC_DO;
if (setsockopt(ls[i].fd, IPPROTO_IP, IP_MTU_DISCOVER,
(const void *) &value, sizeof(int))
== -1)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
"setsockopt(IP_MTU_DISCOVER) "
"for %V failed, ignored",
&ls[i].addr_text);
}
}
#elif (NGX_HAVE_IP_DONTFRAG)
if (ls[i].quic && ls[i].sockaddr->sa_family == AF_INET) {
value = 1;
if (setsockopt(ls[i].fd, IPPROTO_IP, IP_DONTFRAG,
(const void *) &value, sizeof(int))
== -1)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
"setsockopt(IP_DONTFRAG) "
"for %V failed, ignored",
&ls[i].addr_text);
}
}
#endif
#if (NGX_HAVE_INET6)
#if (NGX_HAVE_IPV6_MTU_DISCOVER)
if (ls[i].quic && ls[i].sockaddr->sa_family == AF_INET6) {
value = IPV6_PMTUDISC_DO;
if (setsockopt(ls[i].fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
(const void *) &value, sizeof(int))
== -1)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
"setsockopt(IPV6_MTU_DISCOVER) "
"for %V failed, ignored",
&ls[i].addr_text);
}
}
#elif (NGX_HAVE_IP_DONTFRAG)
if (ls[i].quic && ls[i].sockaddr->sa_family == AF_INET6) {
value = 1;
if (setsockopt(ls[i].fd, IPPROTO_IPV6, IPV6_DONTFRAG,
(const void *) &value, sizeof(int))
== -1)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
"setsockopt(IPV6_DONTFRAG) "
"for %V failed, ignored",
&ls[i].addr_text);
}
}
#endif
#endif
}
@ -1037,6 +1109,12 @@ ngx_close_listening_sockets(ngx_cycle_t *cycle)
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
#if (NGX_QUIC)
if (ls[i].quic) {
continue;
}
#endif
c = ls[i].connection;
if (c) {
@ -1505,6 +1583,10 @@ ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text)
}
#endif
if (err == NGX_EMSGSIZE && c->log_error == NGX_ERROR_IGNORE_EMSGSIZE) {
return 0;
}
if (err == 0
|| err == NGX_ECONNRESET
#if (NGX_WIN32)
@ -1522,6 +1604,7 @@ ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text)
{
switch (c->log_error) {
case NGX_ERROR_IGNORE_EMSGSIZE:
case NGX_ERROR_IGNORE_EINVAL:
case NGX_ERROR_IGNORE_ECONNRESET:
case NGX_ERROR_INFO:

View file

@ -73,6 +73,7 @@ struct ngx_listening_s {
unsigned reuseport:1;
unsigned add_reuseport:1;
unsigned keepalive:2;
unsigned quic:1;
unsigned deferred_accept:1;
unsigned delete_deferred:1;
@ -96,7 +97,8 @@ typedef enum {
NGX_ERROR_ERR,
NGX_ERROR_INFO,
NGX_ERROR_IGNORE_ECONNRESET,
NGX_ERROR_IGNORE_EINVAL
NGX_ERROR_IGNORE_EINVAL,
NGX_ERROR_IGNORE_EMSGSIZE
} ngx_connection_log_error_e;
@ -147,6 +149,10 @@ struct ngx_connection_s {
ngx_proxy_protocol_t *proxy_protocol;
#if (NGX_QUIC || NGX_COMPAT)
ngx_quic_stream_t *quic;
#endif
#if (NGX_SSL || NGX_COMPAT)
ngx_ssl_connection_t *ssl;
#endif

View file

@ -27,6 +27,7 @@ typedef struct ngx_connection_s ngx_connection_t;
typedef struct ngx_thread_task_s ngx_thread_task_t;
typedef struct ngx_ssl_s ngx_ssl_t;
typedef struct ngx_proxy_protocol_s ngx_proxy_protocol_t;
typedef struct ngx_quic_stream_s ngx_quic_stream_t;
typedef struct ngx_ssl_connection_s ngx_ssl_connection_t;
typedef struct ngx_udp_connection_s ngx_udp_connection_t;
@ -82,6 +83,9 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#include <ngx_resolver.h>
#if (NGX_OPENSSL)
#include <ngx_event_openssl.h>
#if (NGX_QUIC)
#include <ngx_event_quic.h>
#endif
#endif
#include <ngx_process_cycle.h>
#include <ngx_conf_file.h>
@ -91,6 +95,9 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#include <ngx_connection.h>
#include <ngx_syslog.h>
#include <ngx_proxy_protocol.h>
#if (NGX_HAVE_BPF)
#include <ngx_bpf.h>
#endif
#define LF (u_char) '\n'

View file

@ -507,7 +507,7 @@ ngx_cidr_match(struct sockaddr *sa, ngx_array_t *cidrs)
p = inaddr6->s6_addr;
inaddr = p[12] << 24;
inaddr = (in_addr_t) p[12] << 24;
inaddr += p[13] << 16;
inaddr += p[14] << 8;
inaddr += p[15];

View file

@ -107,7 +107,12 @@
#endif
#define NGX_MODULE_SIGNATURE_17 "0"
#if (NGX_QUIC || NGX_COMPAT)
#define NGX_MODULE_SIGNATURE_18 "1"
#else
#define NGX_MODULE_SIGNATURE_18 "0"
#endif
#if (NGX_HAVE_OPENAT)
#define NGX_MODULE_SIGNATURE_19 "1"

View file

@ -9,6 +9,10 @@
#include <ngx_core.h>
static void ngx_queue_merge(ngx_queue_t *queue, ngx_queue_t *tail,
ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *));
/*
* find the middle queue element if the queue has odd number of elements
* or the first element of the queue's second part otherwise
@ -45,13 +49,13 @@ ngx_queue_middle(ngx_queue_t *queue)
}
/* the stable insertion sort */
/* the stable merge sort */
void
ngx_queue_sort(ngx_queue_t *queue,
ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))
{
ngx_queue_t *q, *prev, *next;
ngx_queue_t *q, tail;
q = ngx_queue_head(queue);
@ -59,22 +63,44 @@ ngx_queue_sort(ngx_queue_t *queue,
return;
}
for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {
q = ngx_queue_middle(queue);
prev = ngx_queue_prev(q);
next = ngx_queue_next(q);
ngx_queue_split(queue, q, &tail);
ngx_queue_remove(q);
ngx_queue_sort(queue, cmp);
ngx_queue_sort(&tail, cmp);
do {
if (cmp(prev, q) <= 0) {
break;
}
ngx_queue_merge(queue, &tail, cmp);
}
prev = ngx_queue_prev(prev);
} while (prev != ngx_queue_sentinel(queue));
static void
ngx_queue_merge(ngx_queue_t *queue, ngx_queue_t *tail,
ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))
{
ngx_queue_t *q1, *q2;
ngx_queue_insert_after(prev, q);
q1 = ngx_queue_head(queue);
q2 = ngx_queue_head(tail);
for ( ;; ) {
if (q1 == ngx_queue_sentinel(queue)) {
ngx_queue_add(queue, tail);
break;
}
if (q2 == ngx_queue_sentinel(tail)) {
break;
}
if (cmp(q1, q2) <= 0) {
q1 = ngx_queue_next(q1);
continue;
}
ngx_queue_remove(q2);
ngx_queue_insert_before(q1, q2);
q2 = ngx_queue_head(tail);
}
}

View file

@ -47,6 +47,9 @@ struct ngx_queue_s {
(h)->prev = x
#define ngx_queue_insert_before ngx_queue_insert_tail
#define ngx_queue_head(h) \
(h)->next

View file

@ -600,6 +600,8 @@ ngx_regex_cleanup(void *data)
* the new cycle, these will be re-allocated.
*/
ngx_regex_malloc_init(NULL);
if (ngx_regex_compile_context) {
pcre2_compile_context_free(ngx_regex_compile_context);
ngx_regex_compile_context = NULL;
@ -611,6 +613,8 @@ ngx_regex_cleanup(void *data)
ngx_regex_match_data_size = 0;
}
ngx_regex_malloc_done();
#endif
}
@ -706,9 +710,6 @@ ngx_regex_module_init(ngx_cycle_t *cycle)
ngx_regex_malloc_done();
ngx_regex_studies = NULL;
#if (NGX_PCRE2)
ngx_regex_compile_context = NULL;
#endif
return NGX_OK;
}
@ -732,14 +733,14 @@ ngx_regex_create_conf(ngx_cycle_t *cycle)
return NULL;
}
cln->handler = ngx_regex_cleanup;
cln->data = rcf;
rcf->studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t));
if (rcf->studies == NULL) {
return NULL;
}
cln->handler = ngx_regex_cleanup;
cln->data = rcf;
ngx_regex_studies = rcf->studies;
return rcf;

View file

@ -267,6 +267,18 @@ ngx_process_events_and_timers(ngx_cycle_t *cycle)
ngx_int_t
ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)
{
#if (NGX_QUIC)
ngx_connection_t *c;
c = rev->data;
if (c->quic) {
return NGX_OK;
}
#endif
if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
/* kqueue, epoll */
@ -337,9 +349,15 @@ ngx_handle_write_event(ngx_event_t *wev, size_t lowat)
{
ngx_connection_t *c;
if (lowat) {
c = wev->data;
c = wev->data;
#if (NGX_QUIC)
if (c->quic) {
return NGX_OK;
}
#endif
if (lowat) {
if (ngx_send_lowat(c, lowat) == NGX_ERROR) {
return NGX_ERROR;
}
@ -873,8 +891,16 @@ ngx_event_process_init(ngx_cycle_t *cycle)
#else
rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
: ngx_event_recvmsg;
if (c->type == SOCK_STREAM) {
rev->handler = ngx_event_accept;
#if (NGX_QUIC)
} else if (ls[i].quic) {
rev->handler = ngx_quic_recvmsg;
#endif
} else {
rev->handler = ngx_event_recvmsg;
}
#if (NGX_HAVE_REUSEPORT)

View file

@ -33,9 +33,6 @@ static int ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn,
#ifdef SSL_READ_EARLY_DATA_SUCCESS
static ngx_int_t ngx_ssl_try_early_data(ngx_connection_t *c);
#endif
#if (NGX_DEBUG)
static void ngx_ssl_handshake_log(ngx_connection_t *c);
#endif
static void ngx_ssl_handshake_handler(ngx_event_t *ev);
#ifdef SSL_READ_EARLY_DATA_SUCCESS
static ssize_t ngx_ssl_recv_early(ngx_connection_t *c, u_char *buf,
@ -143,13 +140,42 @@ int ngx_ssl_stapling_index;
ngx_int_t
ngx_ssl_init(ngx_log_t *log)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100003L
#if (OPENSSL_INIT_LOAD_CONFIG && !defined LIBRESSL_VERSION_NUMBER)
if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) {
uint64_t opts;
OPENSSL_INIT_SETTINGS *init;
opts = OPENSSL_INIT_LOAD_CONFIG;
#if (NGX_OPENSSL_NO_CONFIG)
if (getenv("OPENSSL_CONF") == NULL) {
opts = OPENSSL_INIT_NO_LOAD_CONFIG;
}
#endif
init = OPENSSL_INIT_new();
if (init == NULL) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "OPENSSL_INIT_new() failed");
return NGX_ERROR;
}
#ifndef OPENSSL_NO_STDIO
if (OPENSSL_INIT_set_config_appname(init, "nginx") == 0) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"OPENSSL_INIT_set_config_appname() failed");
return NGX_ERROR;
}
#endif
if (OPENSSL_init_ssl(opts, init) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "OPENSSL_init_ssl() failed");
return NGX_ERROR;
}
OPENSSL_INIT_free(init);
/*
* OPENSSL_init_ssl() may leave errors in the error queue
* while returning success
@ -159,7 +185,15 @@ ngx_ssl_init(ngx_log_t *log)
#else
OPENSSL_config(NULL);
#if (NGX_OPENSSL_NO_CONFIG)
if (getenv("OPENSSL_CONF") == NULL) {
OPENSSL_no_config();
}
#endif
OPENSSL_config("nginx");
SSL_library_init();
SSL_load_error_strings();
@ -1071,7 +1105,8 @@ ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)
BIO *rbio, *wbio;
ngx_connection_t *c;
#ifndef SSL_OP_NO_RENEGOTIATION
#if (!defined SSL_OP_NO_RENEGOTIATION \
&& !defined SSL_OP_NO_CLIENT_RENEGOTIATION)
if ((where & SSL_CB_HANDSHAKE_START)
&& SSL_is_server((ngx_ssl_conn_t *) ssl_conn))
@ -1804,9 +1839,10 @@ ngx_ssl_handshake(ngx_connection_t *c)
c->read->ready = 1;
c->write->ready = 1;
#ifndef SSL_OP_NO_RENEGOTIATION
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS
#if (!defined SSL_OP_NO_RENEGOTIATION \
&& !defined SSL_OP_NO_CLIENT_RENEGOTIATION \
&& defined SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS \
&& OPENSSL_VERSION_NUMBER < 0x10100000L)
/* initial handshake done, disable renegotiation (CVE-2009-3555) */
if (c->ssl->connection->s3 && SSL_is_server(c->ssl->connection)) {
@ -1814,8 +1850,6 @@ ngx_ssl_handshake(ngx_connection_t *c)
}
#endif
#endif
#endif
#if (defined BIO_get_ktls_send && !NGX_WIN32)
@ -2052,7 +2086,7 @@ ngx_ssl_try_early_data(ngx_connection_t *c)
#if (NGX_DEBUG)
static void
void
ngx_ssl_handshake_log(ngx_connection_t *c)
{
char buf[129], *s, *d;
@ -2449,7 +2483,8 @@ ngx_ssl_handle_recv(ngx_connection_t *c, int n)
int sslerr;
ngx_err_t err;
#ifndef SSL_OP_NO_RENEGOTIATION
#if (!defined SSL_OP_NO_RENEGOTIATION \
&& !defined SSL_OP_NO_CLIENT_RENEGOTIATION)
if (c->ssl->renegotiation) {
/*
@ -3202,6 +3237,13 @@ ngx_ssl_shutdown(ngx_connection_t *c)
ngx_err_t err;
ngx_uint_t tries;
#if (NGX_QUIC)
if (c->quic) {
/* QUIC streams inherit SSL object */
return NGX_OK;
}
#endif
rc = NGX_OK;
ngx_ssl_ocsp_cleanup(c);
@ -5145,6 +5187,9 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
}
curves = ngx_palloc(pool, n * sizeof(int));
if (curves == NULL) {
return NGX_ERROR;
}
n = SSL_get1_curves(c->ssl->connection, curves);
len = 0;

View file

@ -24,6 +24,14 @@
#include <openssl/engine.h>
#endif
#include <openssl/evp.h>
#if (NGX_QUIC)
#ifdef OPENSSL_IS_BORINGSSL
#include <openssl/hkdf.h>
#include <openssl/chacha.h>
#else
#include <openssl/kdf.h>
#endif
#endif
#include <openssl/hmac.h>
#ifndef OPENSSL_NO_OCSP
#include <openssl/ocsp.h>
@ -37,7 +45,7 @@
#if (defined LIBRESSL_VERSION_NUMBER && OPENSSL_VERSION_NUMBER == 0x20000000L)
#undef OPENSSL_VERSION_NUMBER
#if (LIBRESSL_VERSION_NUMBER >= 0x2080000fL)
#if (LIBRESSL_VERSION_NUMBER >= 0x3050000fL)
#define OPENSSL_VERSION_NUMBER 0x1010000fL
#else
#define OPENSSL_VERSION_NUMBER 0x1000107fL
@ -302,6 +310,9 @@ ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool,
ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
#if (NGX_DEBUG)
void ngx_ssl_handshake_log(ngx_connection_t *c);
#endif
ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size);
ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);
ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit);

View file

@ -893,7 +893,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c)
ocsp->cert_status = V_OCSP_CERTSTATUS_GOOD;
ocsp->conf = ocf;
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined LIBRESSL_VERSION_NUMBER)
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
ocsp->certs = SSL_get0_verified_chain(c->ssl->connection);

View file

@ -57,7 +57,9 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write)
do_write = 1;
}
if (p->upstream->fd != (ngx_socket_t) -1) {
if (p->upstream
&& p->upstream->fd != (ngx_socket_t) -1)
{
rev = p->upstream->read;
flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0;
@ -108,7 +110,9 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
ngx_msec_t delay;
ngx_chain_t *chain, *cl, *ln;
if (p->upstream_eof || p->upstream_error || p->upstream_done) {
if (p->upstream_eof || p->upstream_error || p->upstream_done
|| p->upstream == NULL)
{
return NGX_OK;
}

View file

@ -12,13 +12,6 @@
#if !(NGX_WIN32)
struct ngx_udp_connection_s {
ngx_rbtree_node_t node;
ngx_connection_t *connection;
ngx_buf_t *buffer;
};
static void ngx_close_accepted_udp_connection(ngx_connection_t *c);
static ssize_t ngx_udp_shared_recv(ngx_connection_t *c, u_char *buf,
size_t size);
@ -424,8 +417,8 @@ ngx_udp_rbtree_insert_value(ngx_rbtree_node_t *temp,
udpt = (ngx_udp_connection_t *) temp;
ct = udpt->connection;
rc = ngx_cmp_sockaddr(c->sockaddr, c->socklen,
ct->sockaddr, ct->socklen, 1);
rc = ngx_memn2cmp(udp->key.data, udpt->key.data,
udp->key.len, udpt->key.len);
if (rc == 0 && c->listening->wildcard) {
rc = ngx_cmp_sockaddr(c->local_sockaddr, c->local_socklen,
@ -478,6 +471,8 @@ ngx_insert_udp_connection(ngx_connection_t *c)
ngx_crc32_final(hash);
udp->node.key = hash;
udp->key.data = (u_char *) c->sockaddr;
udp->key.len = c->socklen;
cln = ngx_pool_cleanup_add(c->pool, 0);
if (cln == NULL) {

View file

@ -23,6 +23,14 @@
#endif
struct ngx_udp_connection_s {
ngx_rbtree_node_t node;
ngx_connection_t *connection;
ngx_buf_t *buffer;
ngx_str_t key;
};
#if (NGX_HAVE_ADDRINFO_CMSG)
typedef union {

View file

@ -0,0 +1,113 @@
#!/bin/bash
export LANG=C
set -e
if [ $# -lt 1 ]; then
echo "Usage: PROGNAME=foo LICENSE=bar $0 <bpf object file>"
exit 1
fi
self=$0
filename=$1
funcname=$PROGNAME
generate_head()
{
cat << END
/* AUTO-GENERATED, DO NOT EDIT. */
#include <stddef.h>
#include <stdint.h>
#include "ngx_bpf.h"
END
}
generate_tail()
{
cat << END
ngx_bpf_program_t $PROGNAME = {
.relocs = bpf_reloc_prog_$funcname,
.nrelocs = sizeof(bpf_reloc_prog_$funcname)
/ sizeof(bpf_reloc_prog_$funcname[0]),
.ins = bpf_insn_prog_$funcname,
.nins = sizeof(bpf_insn_prog_$funcname)
/ sizeof(bpf_insn_prog_$funcname[0]),
.license = "$LICENSE",
.type = BPF_PROG_TYPE_SK_REUSEPORT,
};
END
}
process_relocations()
{
echo "static ngx_bpf_reloc_t bpf_reloc_prog_$funcname[] = {"
objdump -r $filename | awk '{
if (enabled && $NF > 0) {
off = strtonum(sprintf("0x%s", $1));
name = $3;
printf(" { \"%s\", %d },\n", name, off/8);
}
if ($1 == "OFFSET") {
enabled=1;
}
}'
echo "};"
echo
}
process_section()
{
echo "static struct bpf_insn bpf_insn_prog_$funcname[] = {"
echo " /* opcode dst src offset imm */"
section_info=$(objdump -h $filename --section=$funcname | grep "1 $funcname")
# dd doesn't know hex
length=$(printf "%d" 0x$(echo $section_info | cut -d ' ' -f3))
offset=$(printf "%d" 0x$(echo $section_info | cut -d ' ' -f6))
for ins in $(dd if="$filename" bs=1 count=$length skip=$offset status=none | xxd -p -c 8)
do
opcode=0x${ins:0:2}
srcdst=0x${ins:2:2}
# bytes are dumped in LE order
offset=0x${ins:6:2}${ins:4:2} # short
immedi=0x${ins:14:2}${ins:12:2}${ins:10:2}${ins:8:2} # int
dst="$(($srcdst & 0xF))"
src="$(($srcdst & 0xF0))"
src="$(($src >> 4))"
opcode=$(printf "0x%x" $opcode)
dst=$(printf "BPF_REG_%d" $dst)
src=$(printf "BPF_REG_%d" $src)
offset=$(printf "%d" $offset)
immedi=$(printf "0x%x" $immedi)
printf " { %4s, %11s, %11s, (int16_t) %6s, %10s },\n" $opcode $dst $src $offset $immedi
done
cat << END
};
END
}
generate_head
process_relocations
process_section
generate_tail

View file

@ -0,0 +1,30 @@
CFLAGS=-O2 -Wall
LICENSE=BSD
PROGNAME=ngx_quic_reuseport_helper
RESULT=ngx_event_quic_bpf_code
DEST=../$(RESULT).c
all: $(RESULT)
$(RESULT): $(PROGNAME).o
LICENSE=$(LICENSE) PROGNAME=$(PROGNAME) bash ./bpfgen.sh $< > $@
DEFS=-DPROGNAME=\"$(PROGNAME)\" \
-DLICENSE_$(LICENSE) \
-DLICENSE=\"$(LICENSE)\" \
$(PROGNAME).o: $(PROGNAME).c
clang $(CFLAGS) $(DEFS) -target bpf -c $< -o $@
install: $(RESULT)
cp $(RESULT) $(DEST)
clean:
@rm -f $(RESULT) *.o
debug: $(PROGNAME).o
llvm-objdump -S -no-show-raw-insn $<
.DELETE_ON_ERROR:

View file

@ -0,0 +1,140 @@
#include <errno.h>
#include <linux/string.h>
#include <linux/udp.h>
#include <linux/bpf.h>
/*
* the bpf_helpers.h is not included into linux-headers, only available
* with kernel sources in "tools/lib/bpf/bpf_helpers.h" or in libbpf.
*/
#include <bpf/bpf_helpers.h>
#if !defined(SEC)
#define SEC(NAME) __attribute__((section(NAME), used))
#endif
#if defined(LICENSE_GPL)
/*
* To see debug:
*
* echo 1 > /sys/kernel/debug/tracing/events/bpf_trace/enable
* cat /sys/kernel/debug/tracing/trace_pipe
* echo 0 > /sys/kernel/debug/tracing/events/bpf_trace/enable
*/
#define debugmsg(fmt, ...) \
do { \
char __buf[] = fmt; \
bpf_trace_printk(__buf, sizeof(__buf), ##__VA_ARGS__); \
} while (0)
#else
#define debugmsg(fmt, ...)
#endif
char _license[] SEC("license") = LICENSE;
/*****************************************************************************/
#define NGX_QUIC_PKT_LONG 0x80 /* header form */
#define NGX_QUIC_SERVER_CID_LEN 20
#define advance_data(nbytes) \
offset += nbytes; \
if (start + offset > end) { \
debugmsg("cannot read %ld bytes at offset %ld", nbytes, offset); \
goto failed; \
} \
data = start + offset - 1;
#define ngx_quic_parse_uint64(p) \
(((__u64)(p)[0] << 56) | \
((__u64)(p)[1] << 48) | \
((__u64)(p)[2] << 40) | \
((__u64)(p)[3] << 32) | \
((__u64)(p)[4] << 24) | \
((__u64)(p)[5] << 16) | \
((__u64)(p)[6] << 8) | \
((__u64)(p)[7]))
/*
* actual map object is created by the "bpf" system call,
* all pointers to this variable are replaced by the bpf loader
*/
struct bpf_map_def SEC("maps") ngx_quic_sockmap;
SEC(PROGNAME)
int ngx_quic_select_socket_by_dcid(struct sk_reuseport_md *ctx)
{
int rc;
__u64 key;
size_t len, offset;
unsigned char *start, *end, *data, *dcid;
start = ctx->data;
end = (unsigned char *) ctx->data_end;
offset = 0;
advance_data(sizeof(struct udphdr)); /* data at UDP header */
advance_data(1); /* data at QUIC flags */
if (data[0] & NGX_QUIC_PKT_LONG) {
advance_data(4); /* data at QUIC version */
advance_data(1); /* data at DCID len */
len = data[0]; /* read DCID length */
if (len < 8) {
/* it's useless to search for key in such short DCID */
return SK_PASS;
}
} else {
len = NGX_QUIC_SERVER_CID_LEN;
}
dcid = &data[1];
advance_data(len); /* we expect the packet to have full DCID */
/* make verifier happy */
if (dcid + sizeof(__u64) > end) {
goto failed;
}
key = ngx_quic_parse_uint64(dcid);
rc = bpf_sk_select_reuseport(ctx, &ngx_quic_sockmap, &key, 0);
switch (rc) {
case 0:
debugmsg("nginx quic socket selected by key 0x%llx", key);
return SK_PASS;
/* kernel returns positive error numbers, errno.h defines positive */
case -ENOENT:
debugmsg("nginx quic default route for key 0x%llx", key);
/* let the default reuseport logic decide which socket to choose */
return SK_PASS;
default:
debugmsg("nginx quic bpf_sk_select_reuseport err: %d key 0x%llx",
rc, key);
goto failed;
}
failed:
/*
* SK_DROP will generate ICMP, but we may want to process "invalid" packet
* in userspace quic to investigate further and finally react properly
* (maybe ignore, maybe send something in response or close connection)
*/
return SK_PASS;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,129 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_H_INCLUDED_
#define _NGX_EVENT_QUIC_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#define NGX_QUIC_MAX_UDP_PAYLOAD_SIZE 65527
#define NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT 3
#define NGX_QUIC_DEFAULT_MAX_ACK_DELAY 25
#define NGX_QUIC_DEFAULT_HOST_KEY_LEN 32
#define NGX_QUIC_SR_KEY_LEN 32
#define NGX_QUIC_AV_KEY_LEN 32
#define NGX_QUIC_SR_TOKEN_LEN 16
#define NGX_QUIC_MIN_INITIAL_SIZE 1200
#define NGX_QUIC_STREAM_SERVER_INITIATED 0x01
#define NGX_QUIC_STREAM_UNIDIRECTIONAL 0x02
typedef ngx_int_t (*ngx_quic_init_pt)(ngx_connection_t *c);
typedef void (*ngx_quic_shutdown_pt)(ngx_connection_t *c);
typedef enum {
NGX_QUIC_STREAM_SEND_READY = 0,
NGX_QUIC_STREAM_SEND_SEND,
NGX_QUIC_STREAM_SEND_DATA_SENT,
NGX_QUIC_STREAM_SEND_DATA_RECVD,
NGX_QUIC_STREAM_SEND_RESET_SENT,
NGX_QUIC_STREAM_SEND_RESET_RECVD
} ngx_quic_stream_send_state_e;
typedef enum {
NGX_QUIC_STREAM_RECV_RECV = 0,
NGX_QUIC_STREAM_RECV_SIZE_KNOWN,
NGX_QUIC_STREAM_RECV_DATA_RECVD,
NGX_QUIC_STREAM_RECV_DATA_READ,
NGX_QUIC_STREAM_RECV_RESET_RECVD,
NGX_QUIC_STREAM_RECV_RESET_READ
} ngx_quic_stream_recv_state_e;
typedef struct {
uint64_t size;
uint64_t offset;
uint64_t last_offset;
ngx_chain_t *chain;
ngx_chain_t *last_chain;
} ngx_quic_buffer_t;
typedef struct {
ngx_ssl_t *ssl;
ngx_flag_t retry;
ngx_flag_t gso_enabled;
ngx_flag_t disable_active_migration;
ngx_msec_t handshake_timeout;
ngx_msec_t idle_timeout;
ngx_str_t host_key;
size_t stream_buffer_size;
ngx_uint_t max_concurrent_streams_bidi;
ngx_uint_t max_concurrent_streams_uni;
ngx_uint_t active_connection_id_limit;
ngx_int_t stream_close_code;
ngx_int_t stream_reject_code_uni;
ngx_int_t stream_reject_code_bidi;
ngx_quic_init_pt init;
ngx_quic_shutdown_pt shutdown;
u_char av_token_key[NGX_QUIC_AV_KEY_LEN];
u_char sr_token_key[NGX_QUIC_SR_KEY_LEN];
} ngx_quic_conf_t;
struct ngx_quic_stream_s {
ngx_rbtree_node_t node;
ngx_queue_t queue;
ngx_connection_t *parent;
ngx_connection_t *connection;
uint64_t id;
uint64_t sent;
uint64_t acked;
uint64_t send_max_data;
uint64_t send_offset;
uint64_t send_final_size;
uint64_t recv_max_data;
uint64_t recv_offset;
uint64_t recv_window;
uint64_t recv_last;
uint64_t recv_final_size;
ngx_quic_buffer_t send;
ngx_quic_buffer_t recv;
ngx_quic_stream_send_state_e send_state;
ngx_quic_stream_recv_state_e recv_state;
unsigned cancelable:1;
unsigned fin_acked:1;
};
void ngx_quic_recvmsg(ngx_event_t *ev);
void ngx_quic_run(ngx_connection_t *c, ngx_quic_conf_t *conf);
ngx_connection_t *ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi);
void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t err,
const char *reason);
void ngx_quic_shutdown_connection(ngx_connection_t *c, ngx_uint_t err,
const char *reason);
ngx_int_t ngx_quic_reset_stream(ngx_connection_t *c, ngx_uint_t err);
ngx_int_t ngx_quic_shutdown_stream(ngx_connection_t *c, int how);
void ngx_quic_cancelable_stream(ngx_connection_t *c);
ngx_int_t ngx_quic_get_packet_dcid(ngx_log_t *log, u_char *data, size_t len,
ngx_str_t *dcid);
ngx_int_t ngx_quic_derive_key(ngx_log_t *log, const char *label,
ngx_str_t *secret, ngx_str_t *salt, u_char *out, size_t len);
#endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,30 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_ACK_H_INCLUDED_
#define _NGX_EVENT_QUIC_ACK_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_frame_t *f);
void ngx_quic_congestion_ack(ngx_connection_t *c,
ngx_quic_frame_t *frame);
void ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
void ngx_quic_set_lost_timer(ngx_connection_t *c);
void ngx_quic_pto_handler(ngx_event_t *ev);
ngx_msec_t ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
ngx_int_t ngx_quic_ack_packet(ngx_connection_t *c,
ngx_quic_header_t *pkt);
ngx_int_t ngx_quic_generate_ack(ngx_connection_t *c,
ngx_quic_send_ctx_t *ctx);
#endif /* _NGX_EVENT_QUIC_ACK_H_INCLUDED_ */

View file

@ -0,0 +1,657 @@
/*
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#define NGX_QUIC_BPF_VARNAME "NGINX_BPF_MAPS"
#define NGX_QUIC_BPF_VARSEP ';'
#define NGX_QUIC_BPF_ADDRSEP '#'
#define ngx_quic_bpf_get_conf(cycle) \
(ngx_quic_bpf_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_quic_bpf_module)
#define ngx_quic_bpf_get_old_conf(cycle) \
cycle->old_cycle->conf_ctx ? ngx_quic_bpf_get_conf(cycle->old_cycle) \
: NULL
#define ngx_core_get_conf(cycle) \
(ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module)
typedef struct {
ngx_queue_t queue;
int map_fd;
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_uint_t unused; /* unsigned unused:1; */
} ngx_quic_sock_group_t;
typedef struct {
ngx_flag_t enabled;
ngx_uint_t map_size;
ngx_queue_t groups; /* of ngx_quic_sock_group_t */
} ngx_quic_bpf_conf_t;
static void *ngx_quic_bpf_create_conf(ngx_cycle_t *cycle);
static ngx_int_t ngx_quic_bpf_module_init(ngx_cycle_t *cycle);
static void ngx_quic_bpf_cleanup(void *data);
static ngx_inline void ngx_quic_bpf_close(ngx_log_t *log, int fd,
const char *name);
static ngx_quic_sock_group_t *ngx_quic_bpf_find_group(ngx_quic_bpf_conf_t *bcf,
ngx_listening_t *ls);
static ngx_quic_sock_group_t *ngx_quic_bpf_alloc_group(ngx_cycle_t *cycle,
struct sockaddr *sa, socklen_t socklen);
static ngx_quic_sock_group_t *ngx_quic_bpf_create_group(ngx_cycle_t *cycle,
ngx_listening_t *ls);
static ngx_quic_sock_group_t *ngx_quic_bpf_get_group(ngx_cycle_t *cycle,
ngx_listening_t *ls);
static ngx_int_t ngx_quic_bpf_group_add_socket(ngx_cycle_t *cycle,
ngx_listening_t *ls);
static uint64_t ngx_quic_bpf_socket_key(ngx_fd_t fd, ngx_log_t *log);
static ngx_int_t ngx_quic_bpf_export_maps(ngx_cycle_t *cycle);
static ngx_int_t ngx_quic_bpf_import_maps(ngx_cycle_t *cycle);
extern ngx_bpf_program_t ngx_quic_reuseport_helper;
static ngx_command_t ngx_quic_bpf_commands[] = {
{ ngx_string("quic_bpf"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_quic_bpf_conf_t, enabled),
NULL },
ngx_null_command
};
static ngx_core_module_t ngx_quic_bpf_module_ctx = {
ngx_string("quic_bpf"),
ngx_quic_bpf_create_conf,
NULL
};
ngx_module_t ngx_quic_bpf_module = {
NGX_MODULE_V1,
&ngx_quic_bpf_module_ctx, /* module context */
ngx_quic_bpf_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
ngx_quic_bpf_module_init, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_quic_bpf_create_conf(ngx_cycle_t *cycle)
{
ngx_quic_bpf_conf_t *bcf;
bcf = ngx_pcalloc(cycle->pool, sizeof(ngx_quic_bpf_conf_t));
if (bcf == NULL) {
return NULL;
}
bcf->enabled = NGX_CONF_UNSET;
bcf->map_size = NGX_CONF_UNSET_UINT;
ngx_queue_init(&bcf->groups);
return bcf;
}
static ngx_int_t
ngx_quic_bpf_module_init(ngx_cycle_t *cycle)
{
ngx_uint_t i;
ngx_listening_t *ls;
ngx_core_conf_t *ccf;
ngx_pool_cleanup_t *cln;
ngx_quic_bpf_conf_t *bcf;
if (ngx_test_config) {
/*
* during config test, SO_REUSEPORT socket option is
* not set, thus making further processing meaningless
*/
return NGX_OK;
}
ccf = ngx_core_get_conf(cycle);
bcf = ngx_quic_bpf_get_conf(cycle);
ngx_conf_init_value(bcf->enabled, 0);
bcf->map_size = ccf->worker_processes * 4;
cln = ngx_pool_cleanup_add(cycle->pool, 0);
if (cln == NULL) {
goto failed;
}
cln->data = bcf;
cln->handler = ngx_quic_bpf_cleanup;
if (ngx_inherited && ngx_is_init_cycle(cycle->old_cycle)) {
if (ngx_quic_bpf_import_maps(cycle) != NGX_OK) {
goto failed;
}
}
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
if (ls[i].quic && ls[i].reuseport) {
if (ngx_quic_bpf_group_add_socket(cycle, &ls[i]) != NGX_OK) {
goto failed;
}
}
}
if (ngx_quic_bpf_export_maps(cycle) != NGX_OK) {
goto failed;
}
return NGX_OK;
failed:
if (ngx_is_init_cycle(cycle->old_cycle)) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"ngx_quic_bpf_module failed to initialize, check limits");
/* refuse to start */
return NGX_ERROR;
}
/*
* returning error now will lead to master process exiting immediately
* leaving worker processes orphaned, what is really unexpected.
* Instead, just issue a not about failed initialization and try
* to cleanup a bit. Still program can be already loaded to kernel
* for some reuseport groups, and there is no way to revert, so
* behaviour may be inconsistent.
*/
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"ngx_quic_bpf_module failed to initialize properly, ignored."
"please check limits and note that nginx state now "
"can be inconsistent and restart may be required");
return NGX_OK;
}
static void
ngx_quic_bpf_cleanup(void *data)
{
ngx_quic_bpf_conf_t *bcf = (ngx_quic_bpf_conf_t *) data;
ngx_queue_t *q;
ngx_quic_sock_group_t *grp;
for (q = ngx_queue_head(&bcf->groups);
q != ngx_queue_sentinel(&bcf->groups);
q = ngx_queue_next(q))
{
grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);
ngx_quic_bpf_close(ngx_cycle->log, grp->map_fd, "map");
}
}
static ngx_inline void
ngx_quic_bpf_close(ngx_log_t *log, int fd, const char *name)
{
if (close(fd) != -1) {
return;
}
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"quic bpf close %s fd:%d failed", name, fd);
}
static ngx_quic_sock_group_t *
ngx_quic_bpf_find_group(ngx_quic_bpf_conf_t *bcf, ngx_listening_t *ls)
{
ngx_queue_t *q;
ngx_quic_sock_group_t *grp;
for (q = ngx_queue_head(&bcf->groups);
q != ngx_queue_sentinel(&bcf->groups);
q = ngx_queue_next(q))
{
grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);
if (ngx_cmp_sockaddr(ls->sockaddr, ls->socklen,
grp->sockaddr, grp->socklen, 1)
== NGX_OK)
{
return grp;
}
}
return NULL;
}
static ngx_quic_sock_group_t *
ngx_quic_bpf_alloc_group(ngx_cycle_t *cycle, struct sockaddr *sa,
socklen_t socklen)
{
ngx_quic_bpf_conf_t *bcf;
ngx_quic_sock_group_t *grp;
bcf = ngx_quic_bpf_get_conf(cycle);
grp = ngx_pcalloc(cycle->pool, sizeof(ngx_quic_sock_group_t));
if (grp == NULL) {
return NULL;
}
grp->socklen = socklen;
grp->sockaddr = ngx_palloc(cycle->pool, socklen);
if (grp->sockaddr == NULL) {
return NULL;
}
ngx_memcpy(grp->sockaddr, sa, socklen);
ngx_queue_insert_tail(&bcf->groups, &grp->queue);
return grp;
}
static ngx_quic_sock_group_t *
ngx_quic_bpf_create_group(ngx_cycle_t *cycle, ngx_listening_t *ls)
{
int progfd, failed, flags, rc;
ngx_quic_bpf_conf_t *bcf;
ngx_quic_sock_group_t *grp;
bcf = ngx_quic_bpf_get_conf(cycle);
if (!bcf->enabled) {
return NULL;
}
grp = ngx_quic_bpf_alloc_group(cycle, ls->sockaddr, ls->socklen);
if (grp == NULL) {
return NULL;
}
grp->map_fd = ngx_bpf_map_create(cycle->log, BPF_MAP_TYPE_SOCKHASH,
sizeof(uint64_t), sizeof(uint64_t),
bcf->map_size, 0);
if (grp->map_fd == -1) {
goto failed;
}
flags = fcntl(grp->map_fd, F_GETFD);
if (flags == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, errno,
"quic bpf getfd failed");
goto failed;
}
/* need to inherit map during binary upgrade after exec */
flags &= ~FD_CLOEXEC;
rc = fcntl(grp->map_fd, F_SETFD, flags);
if (rc == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, errno,
"quic bpf setfd failed");
goto failed;
}
ngx_bpf_program_link(&ngx_quic_reuseport_helper,
"ngx_quic_sockmap", grp->map_fd);
progfd = ngx_bpf_load_program(cycle->log, &ngx_quic_reuseport_helper);
if (progfd < 0) {
goto failed;
}
failed = 0;
if (setsockopt(ls->fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF,
&progfd, sizeof(int))
== -1)
{
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
"quic bpf setsockopt(SO_ATTACH_REUSEPORT_EBPF) failed");
failed = 1;
}
ngx_quic_bpf_close(cycle->log, progfd, "program");
if (failed) {
goto failed;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"quic bpf sockmap created fd:%d", grp->map_fd);
return grp;
failed:
if (grp->map_fd != -1) {
ngx_quic_bpf_close(cycle->log, grp->map_fd, "map");
}
ngx_queue_remove(&grp->queue);
return NULL;
}
static ngx_quic_sock_group_t *
ngx_quic_bpf_get_group(ngx_cycle_t *cycle, ngx_listening_t *ls)
{
ngx_quic_bpf_conf_t *bcf, *old_bcf;
ngx_quic_sock_group_t *grp, *ogrp;
bcf = ngx_quic_bpf_get_conf(cycle);
grp = ngx_quic_bpf_find_group(bcf, ls);
if (grp) {
return grp;
}
old_bcf = ngx_quic_bpf_get_old_conf(cycle);
if (old_bcf == NULL) {
return ngx_quic_bpf_create_group(cycle, ls);
}
ogrp = ngx_quic_bpf_find_group(old_bcf, ls);
if (ogrp == NULL) {
return ngx_quic_bpf_create_group(cycle, ls);
}
grp = ngx_quic_bpf_alloc_group(cycle, ls->sockaddr, ls->socklen);
if (grp == NULL) {
return NULL;
}
grp->map_fd = dup(ogrp->map_fd);
if (grp->map_fd == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"quic bpf failed to duplicate bpf map descriptor");
ngx_queue_remove(&grp->queue);
return NULL;
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"quic bpf sockmap fd duplicated old:%d new:%d",
ogrp->map_fd, grp->map_fd);
return grp;
}
static ngx_int_t
ngx_quic_bpf_group_add_socket(ngx_cycle_t *cycle, ngx_listening_t *ls)
{
uint64_t cookie;
ngx_quic_bpf_conf_t *bcf;
ngx_quic_sock_group_t *grp;
bcf = ngx_quic_bpf_get_conf(cycle);
grp = ngx_quic_bpf_get_group(cycle, ls);
if (grp == NULL) {
if (!bcf->enabled) {
return NGX_OK;
}
return NGX_ERROR;
}
grp->unused = 0;
cookie = ngx_quic_bpf_socket_key(ls->fd, cycle->log);
if (cookie == (uint64_t) NGX_ERROR) {
return NGX_ERROR;
}
/* map[cookie] = socket; for use in kernel helper */
if (ngx_bpf_map_update(grp->map_fd, &cookie, &ls->fd, BPF_ANY) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"quic bpf failed to update socket map key=%xL", cookie);
return NGX_ERROR;
}
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"quic bpf sockmap fd:%d add socket:%d cookie:0x%xL worker:%ui",
grp->map_fd, ls->fd, cookie, ls->worker);
/* do not inherit this socket */
ls->ignore = 1;
return NGX_OK;
}
static uint64_t
ngx_quic_bpf_socket_key(ngx_fd_t fd, ngx_log_t *log)
{
uint64_t cookie;
socklen_t optlen;
optlen = sizeof(cookie);
if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &optlen) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
"quic bpf getsockopt(SO_COOKIE) failed");
return (ngx_uint_t) NGX_ERROR;
}
return cookie;
}
static ngx_int_t
ngx_quic_bpf_export_maps(ngx_cycle_t *cycle)
{
u_char *p, *buf;
size_t len;
ngx_str_t *var;
ngx_queue_t *q;
ngx_core_conf_t *ccf;
ngx_quic_bpf_conf_t *bcf;
ngx_quic_sock_group_t *grp;
ccf = ngx_core_get_conf(cycle);
bcf = ngx_quic_bpf_get_conf(cycle);
len = sizeof(NGX_QUIC_BPF_VARNAME) + 1;
q = ngx_queue_head(&bcf->groups);
while (q != ngx_queue_sentinel(&bcf->groups)) {
grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);
q = ngx_queue_next(q);
if (grp->unused) {
/*
* map was inherited, but it is not used in this configuration;
* do not pass such map further and drop the group to prevent
* interference with changes during reload
*/
ngx_quic_bpf_close(cycle->log, grp->map_fd, "map");
ngx_queue_remove(&grp->queue);
continue;
}
len += NGX_INT32_LEN + 1 + NGX_SOCKADDR_STRLEN + 1;
}
len++;
buf = ngx_palloc(cycle->pool, len);
if (buf == NULL) {
return NGX_ERROR;
}
p = ngx_cpymem(buf, NGX_QUIC_BPF_VARNAME "=",
sizeof(NGX_QUIC_BPF_VARNAME));
for (q = ngx_queue_head(&bcf->groups);
q != ngx_queue_sentinel(&bcf->groups);
q = ngx_queue_next(q))
{
grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);
p = ngx_sprintf(p, "%ud", grp->map_fd);
*p++ = NGX_QUIC_BPF_ADDRSEP;
p += ngx_sock_ntop(grp->sockaddr, grp->socklen, p,
NGX_SOCKADDR_STRLEN, 1);
*p++ = NGX_QUIC_BPF_VARSEP;
}
*p = '\0';
var = ngx_array_push(&ccf->env);
if (var == NULL) {
return NGX_ERROR;
}
var->data = buf;
var->len = sizeof(NGX_QUIC_BPF_VARNAME) - 1;
return NGX_OK;
}
static ngx_int_t
ngx_quic_bpf_import_maps(ngx_cycle_t *cycle)
{
int s;
u_char *inherited, *p, *v;
ngx_uint_t in_fd;
ngx_addr_t tmp;
ngx_quic_bpf_conf_t *bcf;
ngx_quic_sock_group_t *grp;
inherited = (u_char *) getenv(NGX_QUIC_BPF_VARNAME);
if (inherited == NULL) {
return NGX_OK;
}
bcf = ngx_quic_bpf_get_conf(cycle);
#if (NGX_SUPPRESS_WARN)
s = -1;
#endif
in_fd = 1;
for (p = inherited, v = p; *p; p++) {
switch (*p) {
case NGX_QUIC_BPF_ADDRSEP:
if (!in_fd) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"quic bpf failed to parse inherited env");
return NGX_ERROR;
}
in_fd = 0;
s = ngx_atoi(v, p - v);
if (s == NGX_ERROR) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"quic bpf failed to parse inherited map fd");
return NGX_ERROR;
}
v = p + 1;
break;
case NGX_QUIC_BPF_VARSEP:
if (in_fd) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"quic bpf failed to parse inherited env");
return NGX_ERROR;
}
in_fd = 1;
grp = ngx_pcalloc(cycle->pool,
sizeof(ngx_quic_sock_group_t));
if (grp == NULL) {
return NGX_ERROR;
}
grp->map_fd = s;
if (ngx_parse_addr_port(cycle->pool, &tmp, v, p - v)
!= NGX_OK)
{
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"quic bpf failed to parse inherited"
" address '%*s'", p - v , v);
ngx_quic_bpf_close(cycle->log, s, "inherited map");
return NGX_ERROR;
}
grp->sockaddr = tmp.sockaddr;
grp->socklen = tmp.socklen;
grp->unused = 1;
ngx_queue_insert_tail(&bcf->groups, &grp->queue);
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"quic bpf sockmap inherited with "
"fd:%d address:%*s",
grp->map_fd, p - v, v);
v = p + 1;
break;
default:
break;
}
}
return NGX_OK;
}

View file

@ -0,0 +1,88 @@
/* AUTO-GENERATED, DO NOT EDIT. */
#include <stddef.h>
#include <stdint.h>
#include "ngx_bpf.h"
static ngx_bpf_reloc_t bpf_reloc_prog_ngx_quic_reuseport_helper[] = {
{ "ngx_quic_sockmap", 55 },
};
static struct bpf_insn bpf_insn_prog_ngx_quic_reuseport_helper[] = {
/* opcode dst src offset imm */
{ 0x79, BPF_REG_4, BPF_REG_1, (int16_t) 0, 0x0 },
{ 0x79, BPF_REG_3, BPF_REG_1, (int16_t) 8, 0x0 },
{ 0xbf, BPF_REG_2, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x7, BPF_REG_2, BPF_REG_0, (int16_t) 0, 0x8 },
{ 0x2d, BPF_REG_2, BPF_REG_3, (int16_t) 54, 0x0 },
{ 0xbf, BPF_REG_5, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x7, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0x9 },
{ 0x2d, BPF_REG_5, BPF_REG_3, (int16_t) 51, 0x0 },
{ 0xb7, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0x14 },
{ 0xb7, BPF_REG_0, BPF_REG_0, (int16_t) 0, 0x9 },
{ 0x71, BPF_REG_6, BPF_REG_2, (int16_t) 0, 0x0 },
{ 0x67, BPF_REG_6, BPF_REG_0, (int16_t) 0, 0x38 },
{ 0xc7, BPF_REG_6, BPF_REG_0, (int16_t) 0, 0x38 },
{ 0x65, BPF_REG_6, BPF_REG_0, (int16_t) 10, 0xffffffff },
{ 0xbf, BPF_REG_2, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x7, BPF_REG_2, BPF_REG_0, (int16_t) 0, 0xd },
{ 0x2d, BPF_REG_2, BPF_REG_3, (int16_t) 42, 0x0 },
{ 0xbf, BPF_REG_5, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x7, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0xe },
{ 0x2d, BPF_REG_5, BPF_REG_3, (int16_t) 39, 0x0 },
{ 0xb7, BPF_REG_0, BPF_REG_0, (int16_t) 0, 0xe },
{ 0x71, BPF_REG_5, BPF_REG_2, (int16_t) 0, 0x0 },
{ 0xb7, BPF_REG_6, BPF_REG_0, (int16_t) 0, 0x8 },
{ 0x2d, BPF_REG_6, BPF_REG_5, (int16_t) 35, 0x0 },
{ 0xf, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0x0 },
{ 0xf, BPF_REG_4, BPF_REG_5, (int16_t) 0, 0x0 },
{ 0x2d, BPF_REG_4, BPF_REG_3, (int16_t) 32, 0x0 },
{ 0xbf, BPF_REG_4, BPF_REG_2, (int16_t) 0, 0x0 },
{ 0x7, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x9 },
{ 0x2d, BPF_REG_4, BPF_REG_3, (int16_t) 29, 0x0 },
{ 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 1, 0x0 },
{ 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x38 },
{ 0x71, BPF_REG_3, BPF_REG_2, (int16_t) 2, 0x0 },
{ 0x67, BPF_REG_3, BPF_REG_0, (int16_t) 0, 0x30 },
{ 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 3, 0x0 },
{ 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x28 },
{ 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 4, 0x0 },
{ 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x20 },
{ 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 5, 0x0 },
{ 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x18 },
{ 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 6, 0x0 },
{ 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x10 },
{ 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 7, 0x0 },
{ 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x8 },
{ 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 },
{ 0x71, BPF_REG_2, BPF_REG_2, (int16_t) 8, 0x0 },
{ 0x4f, BPF_REG_3, BPF_REG_2, (int16_t) 0, 0x0 },
{ 0x7b, BPF_REG_10, BPF_REG_3, (int16_t) 65528, 0x0 },
{ 0xbf, BPF_REG_3, BPF_REG_10, (int16_t) 0, 0x0 },
{ 0x7, BPF_REG_3, BPF_REG_0, (int16_t) 0, 0xfffffff8 },
{ 0x18, BPF_REG_2, BPF_REG_0, (int16_t) 0, 0x0 },
{ 0x0, BPF_REG_0, BPF_REG_0, (int16_t) 0, 0x0 },
{ 0xb7, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x0 },
{ 0x85, BPF_REG_0, BPF_REG_0, (int16_t) 0, 0x52 },
{ 0xb7, BPF_REG_0, BPF_REG_0, (int16_t) 0, 0x1 },
{ 0x95, BPF_REG_0, BPF_REG_0, (int16_t) 0, 0x0 },
};
ngx_bpf_program_t ngx_quic_reuseport_helper = {
.relocs = bpf_reloc_prog_ngx_quic_reuseport_helper,
.nrelocs = sizeof(bpf_reloc_prog_ngx_quic_reuseport_helper)
/ sizeof(bpf_reloc_prog_ngx_quic_reuseport_helper[0]),
.ins = bpf_insn_prog_ngx_quic_reuseport_helper,
.nins = sizeof(bpf_insn_prog_ngx_quic_reuseport_helper)
/ sizeof(bpf_insn_prog_ngx_quic_reuseport_helper[0]),
.license = "BSD",
.type = BPF_PROG_TYPE_SK_REUSEPORT,
};

View file

@ -0,0 +1,305 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_
#define _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
/* #define NGX_QUIC_DEBUG_PACKETS */ /* dump packet contents */
/* #define NGX_QUIC_DEBUG_FRAMES */ /* dump frames contents */
/* #define NGX_QUIC_DEBUG_ALLOC */ /* log frames and bufs alloc */
/* #define NGX_QUIC_DEBUG_CRYPTO */
typedef struct ngx_quic_connection_s ngx_quic_connection_t;
typedef struct ngx_quic_server_id_s ngx_quic_server_id_t;
typedef struct ngx_quic_client_id_s ngx_quic_client_id_t;
typedef struct ngx_quic_send_ctx_s ngx_quic_send_ctx_t;
typedef struct ngx_quic_socket_s ngx_quic_socket_t;
typedef struct ngx_quic_path_s ngx_quic_path_t;
typedef struct ngx_quic_keys_s ngx_quic_keys_t;
#if (NGX_QUIC_OPENSSL_COMPAT)
#include <ngx_event_quic_openssl_compat.h>
#endif
#include <ngx_event_quic_transport.h>
#include <ngx_event_quic_protection.h>
#include <ngx_event_quic_frames.h>
#include <ngx_event_quic_migration.h>
#include <ngx_event_quic_connid.h>
#include <ngx_event_quic_streams.h>
#include <ngx_event_quic_ssl.h>
#include <ngx_event_quic_tokens.h>
#include <ngx_event_quic_ack.h>
#include <ngx_event_quic_output.h>
#include <ngx_event_quic_socket.h>
/* RFC 9002, 6.2.2. Handshakes and New Paths: kInitialRtt */
#define NGX_QUIC_INITIAL_RTT 333 /* ms */
#define NGX_QUIC_UNSET_PN (uint64_t) -1
#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
/* 0-RTT and 1-RTT data exist in the same packet number space,
* so we have 3 packet number spaces:
*
* 0 - Initial
* 1 - Handshake
* 2 - 0-RTT and 1-RTT
*/
#define ngx_quic_get_send_ctx(qc, level) \
((level) == ssl_encryption_initial) ? &((qc)->send_ctx[0]) \
: (((level) == ssl_encryption_handshake) ? &((qc)->send_ctx[1]) \
: &((qc)->send_ctx[2]))
#define ngx_quic_get_connection(c) \
(((c)->udp) ? (((ngx_quic_socket_t *)((c)->udp))->quic) : NULL)
#define ngx_quic_get_socket(c) ((ngx_quic_socket_t *)((c)->udp))
#define ngx_quic_init_rtt(qc) \
(qc)->avg_rtt = NGX_QUIC_INITIAL_RTT; \
(qc)->rttvar = NGX_QUIC_INITIAL_RTT / 2; \
(qc)->min_rtt = NGX_TIMER_INFINITE; \
(qc)->first_rtt = NGX_TIMER_INFINITE; \
(qc)->latest_rtt = 0;
typedef enum {
NGX_QUIC_PATH_IDLE = 0,
NGX_QUIC_PATH_VALIDATING,
NGX_QUIC_PATH_WAITING,
NGX_QUIC_PATH_MTUD
} ngx_quic_path_state_e;
struct ngx_quic_client_id_s {
ngx_queue_t queue;
uint64_t seqnum;
size_t len;
u_char id[NGX_QUIC_CID_LEN_MAX];
u_char sr_token[NGX_QUIC_SR_TOKEN_LEN];
ngx_uint_t used; /* unsigned used:1; */
};
struct ngx_quic_server_id_s {
uint64_t seqnum;
size_t len;
u_char id[NGX_QUIC_CID_LEN_MAX];
};
struct ngx_quic_path_s {
ngx_queue_t queue;
struct sockaddr *sockaddr;
ngx_sockaddr_t sa;
socklen_t socklen;
ngx_quic_client_id_t *cid;
ngx_quic_path_state_e state;
ngx_msec_t expires;
ngx_uint_t tries;
ngx_uint_t tag;
size_t mtu;
size_t mtud;
size_t max_mtu;
off_t sent;
off_t received;
u_char challenge[2][8];
uint64_t seqnum;
uint64_t mtu_pnum[NGX_QUIC_PATH_RETRIES];
ngx_str_t addr_text;
u_char text[NGX_SOCKADDR_STRLEN];
unsigned validated:1;
unsigned mtu_unvalidated:1;
};
struct ngx_quic_socket_s {
ngx_udp_connection_t udp;
ngx_quic_connection_t *quic;
ngx_queue_t queue;
ngx_quic_server_id_t sid;
ngx_sockaddr_t sockaddr;
socklen_t socklen;
ngx_uint_t used; /* unsigned used:1; */
};
typedef struct {
ngx_rbtree_t tree;
ngx_rbtree_node_t sentinel;
ngx_queue_t uninitialized;
ngx_queue_t free;
uint64_t sent;
uint64_t recv_offset;
uint64_t recv_window;
uint64_t recv_last;
uint64_t recv_max_data;
uint64_t send_offset;
uint64_t send_max_data;
uint64_t server_max_streams_uni;
uint64_t server_max_streams_bidi;
uint64_t server_streams_uni;
uint64_t server_streams_bidi;
uint64_t client_max_streams_uni;
uint64_t client_max_streams_bidi;
uint64_t client_streams_uni;
uint64_t client_streams_bidi;
ngx_uint_t initialized;
/* unsigned initialized:1; */
} ngx_quic_streams_t;
typedef struct {
size_t in_flight;
size_t window;
size_t ssthresh;
ngx_msec_t recovery_start;
} ngx_quic_congestion_t;
/*
* RFC 9000, 12.3. Packet Numbers
*
* Conceptually, a packet number space is the context in which a packet
* can be processed and acknowledged. Initial packets can only be sent
* with Initial packet protection keys and acknowledged in packets that
* are also Initial packets.
*/
struct ngx_quic_send_ctx_s {
enum ssl_encryption_level_t level;
ngx_quic_buffer_t crypto;
uint64_t crypto_sent;
uint64_t pnum; /* to be sent */
uint64_t largest_ack; /* received from peer */
uint64_t largest_pn; /* received from peer */
ngx_queue_t frames; /* generated frames */
ngx_queue_t sending; /* frames assigned to pkt */
ngx_queue_t sent; /* frames waiting ACK */
uint64_t pending_ack; /* non sent ack-eliciting */
uint64_t largest_range;
uint64_t first_range;
ngx_msec_t largest_received;
ngx_msec_t ack_delay_start;
ngx_uint_t nranges;
ngx_quic_ack_range_t ranges[NGX_QUIC_MAX_RANGES];
ngx_uint_t send_ack;
};
struct ngx_quic_connection_s {
uint32_t version;
ngx_quic_path_t *path;
ngx_queue_t sockets;
ngx_queue_t paths;
ngx_queue_t client_ids;
ngx_queue_t free_sockets;
ngx_queue_t free_paths;
ngx_queue_t free_client_ids;
ngx_uint_t nsockets;
ngx_uint_t nclient_ids;
uint64_t max_retired_seqnum;
uint64_t client_seqnum;
uint64_t server_seqnum;
uint64_t path_seqnum;
ngx_quic_tp_t tp;
ngx_quic_tp_t ctp;
ngx_quic_send_ctx_t send_ctx[NGX_QUIC_SEND_CTX_LAST];
ngx_quic_keys_t *keys;
ngx_quic_conf_t *conf;
ngx_event_t push;
ngx_event_t pto;
ngx_event_t close;
ngx_event_t path_validation;
ngx_event_t key_update;
ngx_msec_t last_cc;
ngx_msec_t first_rtt;
ngx_msec_t latest_rtt;
ngx_msec_t avg_rtt;
ngx_msec_t min_rtt;
ngx_msec_t rttvar;
ngx_uint_t pto_count;
ngx_queue_t free_frames;
ngx_buf_t *free_bufs;
ngx_buf_t *free_shadow_bufs;
ngx_uint_t nframes;
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_uint_t nbufs;
ngx_uint_t nshadowbufs;
#endif
#if (NGX_QUIC_OPENSSL_COMPAT)
ngx_quic_compat_t *compat;
#endif
ngx_quic_streams_t streams;
ngx_quic_congestion_t congestion;
uint64_t rst_pnum; /* first on validated path */
off_t received;
ngx_uint_t error;
enum ssl_encryption_level_t error_level;
ngx_uint_t error_ftype;
const char *error_reason;
ngx_uint_t shutdown_code;
const char *shutdown_reason;
unsigned error_app:1;
unsigned send_timer_set:1;
unsigned closing:1;
unsigned shutdown:1;
unsigned draining:1;
unsigned key_phase:1;
unsigned validated:1;
unsigned client_tp_done:1;
};
ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c,
ngx_quic_tp_t *ctp);
void ngx_quic_discard_ctx(ngx_connection_t *c,
enum ssl_encryption_level_t level);
void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
void ngx_quic_shutdown_quic(ngx_connection_t *c);
#if (NGX_DEBUG)
void ngx_quic_connstate_dbg(ngx_connection_t *c);
#else
#define ngx_quic_connstate_dbg(c)
#endif
#endif /* _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_ */

View file

@ -0,0 +1,502 @@
/*
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_quic_connection.h>
#define NGX_QUIC_MAX_SERVER_IDS 8
#if (NGX_QUIC_BPF)
static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id);
#endif
static ngx_int_t ngx_quic_retire_client_id(ngx_connection_t *c,
ngx_quic_client_id_t *cid);
static ngx_quic_client_id_t *ngx_quic_alloc_client_id(ngx_connection_t *c,
ngx_quic_connection_t *qc);
static ngx_int_t ngx_quic_send_server_id(ngx_connection_t *c,
ngx_quic_server_id_t *sid);
ngx_int_t
ngx_quic_create_server_id(ngx_connection_t *c, u_char *id)
{
if (RAND_bytes(id, NGX_QUIC_SERVER_CID_LEN) != 1) {
return NGX_ERROR;
}
#if (NGX_QUIC_BPF)
if (ngx_quic_bpf_attach_id(c, id) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"quic bpf failed to generate socket key");
/* ignore error, things still may work */
}
#endif
return NGX_OK;
}
#if (NGX_QUIC_BPF)
static ngx_int_t
ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id)
{
int fd;
uint64_t cookie;
socklen_t optlen;
fd = c->listening->fd;
optlen = sizeof(cookie);
if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &optlen) == -1) {
ngx_log_error(NGX_LOG_ERR, c->log, ngx_socket_errno,
"quic getsockopt(SO_COOKIE) failed");
return NGX_ERROR;
}
ngx_quic_dcid_encode_key(id, cookie);
return NGX_OK;
}
#endif
ngx_int_t
ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
ngx_quic_new_conn_id_frame_t *f)
{
ngx_str_t id;
ngx_queue_t *q;
ngx_quic_frame_t *frame;
ngx_quic_client_id_t *cid, *item;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
if (f->seqnum < qc->max_retired_seqnum) {
/*
* RFC 9000, 19.15. NEW_CONNECTION_ID Frame
*
* An endpoint that receives a NEW_CONNECTION_ID frame with
* a sequence number smaller than the Retire Prior To field
* of a previously received NEW_CONNECTION_ID frame MUST send
* a corresponding RETIRE_CONNECTION_ID frame that retires
* the newly received connection ID, unless it has already
* done so for that sequence number.
*/
frame = ngx_quic_alloc_frame(c);
if (frame == NULL) {
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
frame->u.retire_cid.sequence_number = f->seqnum;
ngx_quic_queue_frame(qc, frame);
goto retire;
}
cid = NULL;
for (q = ngx_queue_head(&qc->client_ids);
q != ngx_queue_sentinel(&qc->client_ids);
q = ngx_queue_next(q))
{
item = ngx_queue_data(q, ngx_quic_client_id_t, queue);
if (item->seqnum == f->seqnum) {
cid = item;
break;
}
}
if (cid) {
/*
* Transmission errors, timeouts, and retransmissions might cause the
* same NEW_CONNECTION_ID frame to be received multiple times.
*/
if (cid->len != f->len
|| ngx_strncmp(cid->id, f->cid, f->len) != 0
|| ngx_strncmp(cid->sr_token, f->srt, NGX_QUIC_SR_TOKEN_LEN) != 0)
{
/*
* ..if a sequence number is used for different connection IDs,
* the endpoint MAY treat that receipt as a connection error
* of type PROTOCOL_VIOLATION.
*/
qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
qc->error_reason = "seqnum refers to different connection id/token";
return NGX_ERROR;
}
} else {
id.data = f->cid;
id.len = f->len;
if (ngx_quic_create_client_id(c, &id, f->seqnum, f->srt) == NULL) {
return NGX_ERROR;
}
}
retire:
if (qc->max_retired_seqnum && f->retire <= qc->max_retired_seqnum) {
/*
* Once a sender indicates a Retire Prior To value, smaller values sent
* in subsequent NEW_CONNECTION_ID frames have no effect. A receiver
* MUST ignore any Retire Prior To fields that do not increase the
* largest received Retire Prior To value.
*/
goto done;
}
qc->max_retired_seqnum = f->retire;
q = ngx_queue_head(&qc->client_ids);
while (q != ngx_queue_sentinel(&qc->client_ids)) {
cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
q = ngx_queue_next(q);
if (cid->seqnum >= f->retire) {
continue;
}
if (ngx_quic_retire_client_id(c, cid) != NGX_OK) {
return NGX_ERROR;
}
}
done:
if (qc->nclient_ids > qc->tp.active_connection_id_limit) {
/*
* RFC 9000, 5.1.1. Issuing Connection IDs
*
* After processing a NEW_CONNECTION_ID frame and
* adding and retiring active connection IDs, if the number of active
* connection IDs exceeds the value advertised in its
* active_connection_id_limit transport parameter, an endpoint MUST
* close the connection with an error of type CONNECTION_ID_LIMIT_ERROR.
*/
qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR;
qc->error_reason = "too many connection ids received";
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_quic_retire_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
{
ngx_queue_t *q;
ngx_quic_path_t *path;
ngx_quic_client_id_t *new_cid;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
if (!cid->used) {
return ngx_quic_free_client_id(c, cid);
}
/* we are going to retire client id which is in use */
q = ngx_queue_head(&qc->paths);
while (q != ngx_queue_sentinel(&qc->paths)) {
path = ngx_queue_data(q, ngx_quic_path_t, queue);
q = ngx_queue_next(q);
if (path->cid != cid) {
continue;
}
if (path == qc->path) {
/* this is the active path: update it with new CID */
new_cid = ngx_quic_next_client_id(c);
if (new_cid == NULL) {
return NGX_ERROR;
}
qc->path->cid = new_cid;
new_cid->used = 1;
return ngx_quic_free_client_id(c, cid);
}
return ngx_quic_free_path(c, path);
}
return NGX_OK;
}
static ngx_quic_client_id_t *
ngx_quic_alloc_client_id(ngx_connection_t *c, ngx_quic_connection_t *qc)
{
ngx_queue_t *q;
ngx_quic_client_id_t *cid;
if (!ngx_queue_empty(&qc->free_client_ids)) {
q = ngx_queue_head(&qc->free_client_ids);
cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
ngx_queue_remove(&cid->queue);
ngx_memzero(cid, sizeof(ngx_quic_client_id_t));
} else {
cid = ngx_pcalloc(c->pool, sizeof(ngx_quic_client_id_t));
if (cid == NULL) {
return NULL;
}
}
return cid;
}
ngx_quic_client_id_t *
ngx_quic_create_client_id(ngx_connection_t *c, ngx_str_t *id,
uint64_t seqnum, u_char *token)
{
ngx_quic_client_id_t *cid;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
cid = ngx_quic_alloc_client_id(c, qc);
if (cid == NULL) {
return NULL;
}
cid->seqnum = seqnum;
cid->len = id->len;
ngx_memcpy(cid->id, id->data, id->len);
if (token) {
ngx_memcpy(cid->sr_token, token, NGX_QUIC_SR_TOKEN_LEN);
}
ngx_queue_insert_tail(&qc->client_ids, &cid->queue);
qc->nclient_ids++;
if (seqnum > qc->client_seqnum) {
qc->client_seqnum = seqnum;
}
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic cid seq:%uL received id:%uz:%xV:%*xs",
cid->seqnum, id->len, id,
(size_t) NGX_QUIC_SR_TOKEN_LEN, cid->sr_token);
return cid;
}
ngx_quic_client_id_t *
ngx_quic_next_client_id(ngx_connection_t *c)
{
ngx_queue_t *q;
ngx_quic_client_id_t *cid;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
for (q = ngx_queue_head(&qc->client_ids);
q != ngx_queue_sentinel(&qc->client_ids);
q = ngx_queue_next(q))
{
cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
if (!cid->used) {
return cid;
}
}
return NULL;
}
ngx_int_t
ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c,
ngx_quic_retire_cid_frame_t *f)
{
ngx_quic_socket_t *qsock;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
if (f->sequence_number >= qc->server_seqnum) {
/*
* RFC 9000, 19.16.
*
* Receipt of a RETIRE_CONNECTION_ID frame containing a sequence
* number greater than any previously sent to the peer MUST be
* treated as a connection error of type PROTOCOL_VIOLATION.
*/
qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
qc->error_reason = "sequence number of id to retire was never issued";
return NGX_ERROR;
}
qsock = ngx_quic_get_socket(c);
if (qsock->sid.seqnum == f->sequence_number) {
/*
* RFC 9000, 19.16.
*
* The sequence number specified in a RETIRE_CONNECTION_ID frame MUST
* NOT refer to the Destination Connection ID field of the packet in
* which the frame is contained. The peer MAY treat this as a
* connection error of type PROTOCOL_VIOLATION.
*/
qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
qc->error_reason = "sequence number of id to retire refers DCID";
return NGX_ERROR;
}
qsock = ngx_quic_find_socket(c, f->sequence_number);
if (qsock == NULL) {
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic socket seq:%uL is retired", qsock->sid.seqnum);
ngx_quic_close_socket(c, qsock);
/* restore socket count up to a limit after deletion */
if (ngx_quic_create_sockets(c) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
ngx_int_t
ngx_quic_create_sockets(ngx_connection_t *c)
{
ngx_uint_t n;
ngx_quic_socket_t *qsock;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
n = ngx_min(NGX_QUIC_MAX_SERVER_IDS, qc->ctp.active_connection_id_limit);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic create sockets has:%ui max:%ui", qc->nsockets, n);
while (qc->nsockets < n) {
qsock = ngx_quic_create_socket(c, qc);
if (qsock == NULL) {
return NGX_ERROR;
}
if (ngx_quic_listen(c, qc, qsock) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_quic_send_server_id(c, &qsock->sid) != NGX_OK) {
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_quic_send_server_id(ngx_connection_t *c, ngx_quic_server_id_t *sid)
{
ngx_str_t dcid;
ngx_quic_frame_t *frame;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
dcid.len = sid->len;
dcid.data = sid->id;
frame = ngx_quic_alloc_frame(c);
if (frame == NULL) {
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->type = NGX_QUIC_FT_NEW_CONNECTION_ID;
frame->u.ncid.seqnum = sid->seqnum;
frame->u.ncid.retire = 0;
frame->u.ncid.len = NGX_QUIC_SERVER_CID_LEN;
ngx_memcpy(frame->u.ncid.cid, sid->id, NGX_QUIC_SERVER_CID_LEN);
if (ngx_quic_new_sr_token(c, &dcid, qc->conf->sr_token_key,
frame->u.ncid.srt)
!= NGX_OK)
{
return NGX_ERROR;
}
ngx_quic_queue_frame(qc, frame);
return NGX_OK;
}
ngx_int_t
ngx_quic_free_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
{
ngx_quic_frame_t *frame;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
frame = ngx_quic_alloc_frame(c);
if (frame == NULL) {
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
frame->u.retire_cid.sequence_number = cid->seqnum;
ngx_quic_queue_frame(qc, frame);
/* we are no longer going to use this client id */
ngx_queue_remove(&cid->queue);
ngx_queue_insert_head(&qc->free_client_ids, &cid->queue);
qc->nclient_ids--;
return NGX_OK;
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_CONNID_H_INCLUDED_
#define _NGX_EVENT_QUIC_CONNID_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
ngx_int_t ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c,
ngx_quic_retire_cid_frame_t *f);
ngx_int_t ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
ngx_quic_new_conn_id_frame_t *f);
ngx_int_t ngx_quic_create_sockets(ngx_connection_t *c);
ngx_int_t ngx_quic_create_server_id(ngx_connection_t *c, u_char *id);
ngx_quic_client_id_t *ngx_quic_create_client_id(ngx_connection_t *c,
ngx_str_t *id, uint64_t seqnum, u_char *token);
ngx_quic_client_id_t *ngx_quic_next_client_id(ngx_connection_t *c);
ngx_int_t ngx_quic_free_client_id(ngx_connection_t *c,
ngx_quic_client_id_t *cid);
#endif /* _NGX_EVENT_QUIC_CONNID_H_INCLUDED_ */

View file

@ -0,0 +1,894 @@
/*
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_quic_connection.h>
#define NGX_QUIC_BUFFER_SIZE 4096
#define ngx_quic_buf_refs(b) (b)->shadow->num
#define ngx_quic_buf_inc_refs(b) ngx_quic_buf_refs(b)++
#define ngx_quic_buf_dec_refs(b) ngx_quic_buf_refs(b)--
#define ngx_quic_buf_set_refs(b, v) ngx_quic_buf_refs(b) = v
static ngx_buf_t *ngx_quic_alloc_buf(ngx_connection_t *c);
static void ngx_quic_free_buf(ngx_connection_t *c, ngx_buf_t *b);
static ngx_buf_t *ngx_quic_clone_buf(ngx_connection_t *c, ngx_buf_t *b);
static ngx_int_t ngx_quic_split_chain(ngx_connection_t *c, ngx_chain_t *cl,
off_t offset);
static ngx_buf_t *
ngx_quic_alloc_buf(ngx_connection_t *c)
{
u_char *p;
ngx_buf_t *b;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
b = qc->free_bufs;
if (b) {
qc->free_bufs = b->shadow;
p = b->start;
} else {
b = qc->free_shadow_bufs;
if (b) {
qc->free_shadow_bufs = b->shadow;
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic use shadow buffer n:%ui %ui",
++qc->nbufs, --qc->nshadowbufs);
#endif
} else {
b = ngx_palloc(c->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NULL;
}
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic new buffer n:%ui", ++qc->nbufs);
#endif
}
p = ngx_pnalloc(c->pool, NGX_QUIC_BUFFER_SIZE);
if (p == NULL) {
return NULL;
}
}
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic alloc buffer %p", b);
#endif
ngx_memzero(b, sizeof(ngx_buf_t));
b->tag = (ngx_buf_tag_t) &ngx_quic_alloc_buf;
b->temporary = 1;
b->shadow = b;
b->start = p;
b->pos = p;
b->last = p;
b->end = p + NGX_QUIC_BUFFER_SIZE;
ngx_quic_buf_set_refs(b, 1);
return b;
}
static void
ngx_quic_free_buf(ngx_connection_t *c, ngx_buf_t *b)
{
ngx_buf_t *shadow;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
ngx_quic_buf_dec_refs(b);
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic free buffer %p r:%ui",
b, (ngx_uint_t) ngx_quic_buf_refs(b));
#endif
shadow = b->shadow;
if (ngx_quic_buf_refs(b) == 0) {
shadow->shadow = qc->free_bufs;
qc->free_bufs = shadow;
}
if (b != shadow) {
b->shadow = qc->free_shadow_bufs;
qc->free_shadow_bufs = b;
}
}
static ngx_buf_t *
ngx_quic_clone_buf(ngx_connection_t *c, ngx_buf_t *b)
{
ngx_buf_t *nb;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
nb = qc->free_shadow_bufs;
if (nb) {
qc->free_shadow_bufs = nb->shadow;
} else {
nb = ngx_palloc(c->pool, sizeof(ngx_buf_t));
if (nb == NULL) {
return NULL;
}
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic new shadow buffer n:%ui", ++qc->nshadowbufs);
#endif
}
*nb = *b;
ngx_quic_buf_inc_refs(b);
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic clone buffer %p %p r:%ui",
b, nb, (ngx_uint_t) ngx_quic_buf_refs(b));
#endif
return nb;
}
static ngx_int_t
ngx_quic_split_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t offset)
{
ngx_buf_t *b, *tb;
ngx_chain_t *tail;
b = cl->buf;
tail = ngx_alloc_chain_link(c->pool);
if (tail == NULL) {
return NGX_ERROR;
}
tb = ngx_quic_clone_buf(c, b);
if (tb == NULL) {
return NGX_ERROR;
}
tail->buf = tb;
tb->pos += offset;
b->last = tb->pos;
b->last_buf = 0;
tail->next = cl->next;
cl->next = tail;
return NGX_OK;
}
ngx_quic_frame_t *
ngx_quic_alloc_frame(ngx_connection_t *c)
{
ngx_queue_t *q;
ngx_quic_frame_t *frame;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
if (!ngx_queue_empty(&qc->free_frames)) {
q = ngx_queue_head(&qc->free_frames);
frame = ngx_queue_data(q, ngx_quic_frame_t, queue);
ngx_queue_remove(&frame->queue);
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic reuse frame n:%ui", qc->nframes);
#endif
} else if (qc->nframes < 10000) {
frame = ngx_palloc(c->pool, sizeof(ngx_quic_frame_t));
if (frame == NULL) {
return NULL;
}
++qc->nframes;
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic alloc frame n:%ui", qc->nframes);
#endif
} else {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic flood detected");
return NULL;
}
ngx_memzero(frame, sizeof(ngx_quic_frame_t));
return frame;
}
void
ngx_quic_free_frame(ngx_connection_t *c, ngx_quic_frame_t *frame)
{
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
if (frame->data) {
ngx_quic_free_chain(c, frame->data);
}
ngx_queue_insert_head(&qc->free_frames, &frame->queue);
#ifdef NGX_QUIC_DEBUG_ALLOC
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic free frame n:%ui", qc->nframes);
#endif
}
void
ngx_quic_free_chain(ngx_connection_t *c, ngx_chain_t *in)
{
ngx_chain_t *cl;
while (in) {
cl = in;
in = in->next;
ngx_quic_free_buf(c, cl->buf);
ngx_free_chain(c->pool, cl);
}
}
void
ngx_quic_free_frames(ngx_connection_t *c, ngx_queue_t *frames)
{
ngx_queue_t *q;
ngx_quic_frame_t *f;
do {
q = ngx_queue_head(frames);
if (q == ngx_queue_sentinel(frames)) {
break;
}
ngx_queue_remove(q);
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
ngx_quic_free_frame(c, f);
} while (1);
}
void
ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame)
{
ngx_quic_send_ctx_t *ctx;
ctx = ngx_quic_get_send_ctx(qc, frame->level);
ngx_queue_insert_tail(&ctx->frames, &frame->queue);
frame->len = ngx_quic_create_frame(NULL, frame);
/* always succeeds */
if (qc->closing) {
return;
}
ngx_post_event(&qc->push, &ngx_posted_events);
}
ngx_int_t
ngx_quic_split_frame(ngx_connection_t *c, ngx_quic_frame_t *f, size_t len)
{
size_t shrink;
ngx_chain_t *out;
ngx_quic_frame_t *nf;
ngx_quic_buffer_t qb;
ngx_quic_ordered_frame_t *of, *onf;
switch (f->type) {
case NGX_QUIC_FT_CRYPTO:
case NGX_QUIC_FT_STREAM:
break;
default:
return NGX_DECLINED;
}
if ((size_t) f->len <= len) {
return NGX_OK;
}
shrink = f->len - len;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic split frame now:%uz need:%uz shrink:%uz",
f->len, len, shrink);
of = &f->u.ord;
if (of->length <= shrink) {
return NGX_DECLINED;
}
of->length -= shrink;
f->len = ngx_quic_create_frame(NULL, f);
if ((size_t) f->len > len) {
ngx_log_error(NGX_LOG_ERR, c->log, 0, "could not split QUIC frame");
return NGX_ERROR;
}
ngx_memzero(&qb, sizeof(ngx_quic_buffer_t));
qb.chain = f->data;
out = ngx_quic_read_buffer(c, &qb, of->length);
if (out == NGX_CHAIN_ERROR) {
return NGX_ERROR;
}
f->data = out;
nf = ngx_quic_alloc_frame(c);
if (nf == NULL) {
return NGX_ERROR;
}
*nf = *f;
onf = &nf->u.ord;
onf->offset += of->length;
onf->length = shrink;
nf->len = ngx_quic_create_frame(NULL, nf);
nf->data = qb.chain;
if (f->type == NGX_QUIC_FT_STREAM) {
f->u.stream.fin = 0;
}
ngx_queue_insert_after(&f->queue, &nf->queue);
return NGX_OK;
}
ngx_chain_t *
ngx_quic_copy_buffer(ngx_connection_t *c, u_char *data, size_t len)
{
ngx_buf_t buf;
ngx_chain_t cl, *out;
ngx_quic_buffer_t qb;
ngx_memzero(&buf, sizeof(ngx_buf_t));
buf.pos = data;
buf.last = buf.pos + len;
buf.temporary = 1;
cl.buf = &buf;
cl.next = NULL;
ngx_memzero(&qb, sizeof(ngx_quic_buffer_t));
if (ngx_quic_write_buffer(c, &qb, &cl, len, 0) == NGX_CHAIN_ERROR) {
return NGX_CHAIN_ERROR;
}
out = ngx_quic_read_buffer(c, &qb, len);
if (out == NGX_CHAIN_ERROR) {
return NGX_CHAIN_ERROR;
}
ngx_quic_free_buffer(c, &qb);
return out;
}
ngx_chain_t *
ngx_quic_read_buffer(ngx_connection_t *c, ngx_quic_buffer_t *qb, uint64_t limit)
{
uint64_t n;
ngx_buf_t *b;
ngx_chain_t *out, **ll;
out = qb->chain;
for (ll = &out; *ll; ll = &(*ll)->next) {
b = (*ll)->buf;
if (b->sync) {
/* hole */
break;
}
if (limit == 0) {
break;
}
n = b->last - b->pos;
if (n > limit) {
if (ngx_quic_split_chain(c, *ll, limit) != NGX_OK) {
return NGX_CHAIN_ERROR;
}
n = limit;
}
limit -= n;
qb->offset += n;
}
if (qb->offset >= qb->last_offset) {
qb->last_chain = NULL;
}
qb->chain = *ll;
*ll = NULL;
return out;
}
void
ngx_quic_skip_buffer(ngx_connection_t *c, ngx_quic_buffer_t *qb,
uint64_t offset)
{
size_t n;
ngx_buf_t *b;
ngx_chain_t *cl;
while (qb->chain) {
if (qb->offset >= offset) {
break;
}
cl = qb->chain;
b = cl->buf;
n = b->last - b->pos;
if (qb->offset + n > offset) {
n = offset - qb->offset;
b->pos += n;
qb->offset += n;
break;
}
qb->offset += n;
qb->chain = cl->next;
cl->next = NULL;
ngx_quic_free_chain(c, cl);
}
if (qb->chain == NULL) {
qb->offset = offset;
}
if (qb->offset >= qb->last_offset) {
qb->last_chain = NULL;
}
}
ngx_chain_t *
ngx_quic_alloc_chain(ngx_connection_t *c)
{
ngx_chain_t *cl;
cl = ngx_alloc_chain_link(c->pool);
if (cl == NULL) {
return NULL;
}
cl->buf = ngx_quic_alloc_buf(c);
if (cl->buf == NULL) {
return NULL;
}
return cl;
}
ngx_chain_t *
ngx_quic_write_buffer(ngx_connection_t *c, ngx_quic_buffer_t *qb,
ngx_chain_t *in, uint64_t limit, uint64_t offset)
{
u_char *p;
uint64_t n, base;
ngx_buf_t *b;
ngx_chain_t *cl, **chain;
if (qb->last_chain && offset >= qb->last_offset) {
base = qb->last_offset;
chain = &qb->last_chain;
} else {
base = qb->offset;
chain = &qb->chain;
}
while (in && limit) {
if (offset < base) {
n = ngx_min((uint64_t) (in->buf->last - in->buf->pos),
ngx_min(base - offset, limit));
in->buf->pos += n;
offset += n;
limit -= n;
if (in->buf->pos == in->buf->last) {
in = in->next;
}
continue;
}
cl = *chain;
if (cl == NULL) {
cl = ngx_quic_alloc_chain(c);
if (cl == NULL) {
return NGX_CHAIN_ERROR;
}
cl->buf->last = cl->buf->end;
cl->buf->sync = 1; /* hole */
cl->next = NULL;
*chain = cl;
}
b = cl->buf;
n = b->last - b->pos;
if (base + n <= offset) {
base += n;
chain = &cl->next;
continue;
}
if (b->sync && offset > base) {
if (ngx_quic_split_chain(c, cl, offset - base) != NGX_OK) {
return NGX_CHAIN_ERROR;
}
continue;
}
p = b->pos + (offset - base);
while (in) {
if (!ngx_buf_in_memory(in->buf) || in->buf->pos == in->buf->last) {
in = in->next;
continue;
}
if (p == b->last || limit == 0) {
break;
}
n = ngx_min(b->last - p, in->buf->last - in->buf->pos);
n = ngx_min(n, limit);
if (b->sync) {
ngx_memcpy(p, in->buf->pos, n);
qb->size += n;
}
p += n;
in->buf->pos += n;
offset += n;
limit -= n;
}
if (b->sync && p == b->last) {
b->sync = 0;
continue;
}
if (b->sync && p != b->pos) {
if (ngx_quic_split_chain(c, cl, p - b->pos) != NGX_OK) {
return NGX_CHAIN_ERROR;
}
b->sync = 0;
}
}
qb->last_offset = base;
qb->last_chain = *chain;
return in;
}
void
ngx_quic_free_buffer(ngx_connection_t *c, ngx_quic_buffer_t *qb)
{
ngx_quic_free_chain(c, qb->chain);
qb->chain = NULL;
}
#if (NGX_DEBUG)
void
ngx_quic_log_frame(ngx_log_t *log, ngx_quic_frame_t *f, ngx_uint_t tx)
{
u_char *p, *last, *pos, *end;
ssize_t n;
uint64_t gap, range, largest, smallest;
ngx_uint_t i;
u_char buf[NGX_MAX_ERROR_STR];
p = buf;
last = buf + sizeof(buf);
switch (f->type) {
case NGX_QUIC_FT_CRYPTO:
p = ngx_slprintf(p, last, "CRYPTO len:%uL off:%uL",
f->u.crypto.length, f->u.crypto.offset);
#ifdef NGX_QUIC_DEBUG_FRAMES
{
ngx_chain_t *cl;
p = ngx_slprintf(p, last, " data:");
for (cl = f->data; cl; cl = cl->next) {
p = ngx_slprintf(p, last, "%*xs",
cl->buf->last - cl->buf->pos, cl->buf->pos);
}
}
#endif
break;
case NGX_QUIC_FT_PADDING:
p = ngx_slprintf(p, last, "PADDING");
break;
case NGX_QUIC_FT_ACK:
case NGX_QUIC_FT_ACK_ECN:
p = ngx_slprintf(p, last, "ACK n:%ui delay:%uL ",
f->u.ack.range_count, f->u.ack.delay);
if (f->data) {
pos = f->data->buf->pos;
end = f->data->buf->last;
} else {
pos = NULL;
end = NULL;
}
largest = f->u.ack.largest;
smallest = f->u.ack.largest - f->u.ack.first_range;
if (largest == smallest) {
p = ngx_slprintf(p, last, "%uL", largest);
} else {
p = ngx_slprintf(p, last, "%uL-%uL", largest, smallest);
}
for (i = 0; i < f->u.ack.range_count; i++) {
n = ngx_quic_parse_ack_range(log, pos, end, &gap, &range);
if (n == NGX_ERROR) {
break;
}
pos += n;
largest = smallest - gap - 2;
smallest = largest - range;
if (largest == smallest) {
p = ngx_slprintf(p, last, " %uL", largest);
} else {
p = ngx_slprintf(p, last, " %uL-%uL", largest, smallest);
}
}
if (f->type == NGX_QUIC_FT_ACK_ECN) {
p = ngx_slprintf(p, last, " ECN counters ect0:%uL ect1:%uL ce:%uL",
f->u.ack.ect0, f->u.ack.ect1, f->u.ack.ce);
}
break;
case NGX_QUIC_FT_PING:
p = ngx_slprintf(p, last, "PING");
break;
case NGX_QUIC_FT_NEW_CONNECTION_ID:
p = ngx_slprintf(p, last,
"NEW_CONNECTION_ID seq:%uL retire:%uL len:%ud",
f->u.ncid.seqnum, f->u.ncid.retire, f->u.ncid.len);
break;
case NGX_QUIC_FT_RETIRE_CONNECTION_ID:
p = ngx_slprintf(p, last, "RETIRE_CONNECTION_ID seqnum:%uL",
f->u.retire_cid.sequence_number);
break;
case NGX_QUIC_FT_CONNECTION_CLOSE:
case NGX_QUIC_FT_CONNECTION_CLOSE_APP:
p = ngx_slprintf(p, last, "CONNECTION_CLOSE%s err:%ui",
f->type == NGX_QUIC_FT_CONNECTION_CLOSE ? "" : "_APP",
f->u.close.error_code);
if (f->u.close.reason.len) {
p = ngx_slprintf(p, last, " %V", &f->u.close.reason);
}
if (f->type == NGX_QUIC_FT_CONNECTION_CLOSE) {
p = ngx_slprintf(p, last, " ft:%ui", f->u.close.frame_type);
}
break;
case NGX_QUIC_FT_STREAM:
p = ngx_slprintf(p, last, "STREAM id:0x%xL", f->u.stream.stream_id);
if (f->u.stream.off) {
p = ngx_slprintf(p, last, " off:%uL", f->u.stream.offset);
}
if (f->u.stream.len) {
p = ngx_slprintf(p, last, " len:%uL", f->u.stream.length);
}
if (f->u.stream.fin) {
p = ngx_slprintf(p, last, " fin:1");
}
#ifdef NGX_QUIC_DEBUG_FRAMES
{
ngx_chain_t *cl;
p = ngx_slprintf(p, last, " data:");
for (cl = f->data; cl; cl = cl->next) {
p = ngx_slprintf(p, last, "%*xs",
cl->buf->last - cl->buf->pos, cl->buf->pos);
}
}
#endif
break;
case NGX_QUIC_FT_MAX_DATA:
p = ngx_slprintf(p, last, "MAX_DATA max_data:%uL on recv",
f->u.max_data.max_data);
break;
case NGX_QUIC_FT_RESET_STREAM:
p = ngx_slprintf(p, last, "RESET_STREAM"
" id:0x%xL error_code:0x%xL final_size:0x%xL",
f->u.reset_stream.id, f->u.reset_stream.error_code,
f->u.reset_stream.final_size);
break;
case NGX_QUIC_FT_STOP_SENDING:
p = ngx_slprintf(p, last, "STOP_SENDING id:0x%xL err:0x%xL",
f->u.stop_sending.id, f->u.stop_sending.error_code);
break;
case NGX_QUIC_FT_STREAMS_BLOCKED:
case NGX_QUIC_FT_STREAMS_BLOCKED2:
p = ngx_slprintf(p, last, "STREAMS_BLOCKED limit:%uL bidi:%ui",
f->u.streams_blocked.limit, f->u.streams_blocked.bidi);
break;
case NGX_QUIC_FT_MAX_STREAMS:
case NGX_QUIC_FT_MAX_STREAMS2:
p = ngx_slprintf(p, last, "MAX_STREAMS limit:%uL bidi:%ui",
f->u.max_streams.limit, f->u.max_streams.bidi);
break;
case NGX_QUIC_FT_MAX_STREAM_DATA:
p = ngx_slprintf(p, last, "MAX_STREAM_DATA id:0x%xL limit:%uL",
f->u.max_stream_data.id, f->u.max_stream_data.limit);
break;
case NGX_QUIC_FT_DATA_BLOCKED:
p = ngx_slprintf(p, last, "DATA_BLOCKED limit:%uL",
f->u.data_blocked.limit);
break;
case NGX_QUIC_FT_STREAM_DATA_BLOCKED:
p = ngx_slprintf(p, last, "STREAM_DATA_BLOCKED id:0x%xL limit:%uL",
f->u.stream_data_blocked.id,
f->u.stream_data_blocked.limit);
break;
case NGX_QUIC_FT_PATH_CHALLENGE:
p = ngx_slprintf(p, last, "PATH_CHALLENGE data:0x%*xs",
sizeof(f->u.path_challenge.data),
f->u.path_challenge.data);
break;
case NGX_QUIC_FT_PATH_RESPONSE:
p = ngx_slprintf(p, last, "PATH_RESPONSE data:0x%*xs",
sizeof(f->u.path_challenge.data),
f->u.path_challenge.data);
break;
case NGX_QUIC_FT_NEW_TOKEN:
p = ngx_slprintf(p, last, "NEW_TOKEN");
#ifdef NGX_QUIC_DEBUG_FRAMES
{
ngx_chain_t *cl;
p = ngx_slprintf(p, last, " token:");
for (cl = f->data; cl; cl = cl->next) {
p = ngx_slprintf(p, last, "%*xs",
cl->buf->last - cl->buf->pos, cl->buf->pos);
}
}
#endif
break;
case NGX_QUIC_FT_HANDSHAKE_DONE:
p = ngx_slprintf(p, last, "HANDSHAKE DONE");
break;
default:
p = ngx_slprintf(p, last, "unknown type 0x%xi", f->type);
break;
}
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, log, 0, "quic frame %s %s:%uL %*s",
tx ? "tx" : "rx", ngx_quic_level_name(f->level), f->pnum,
p - buf, buf);
}
#endif

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_FRAMES_H_INCLUDED_
#define _NGX_EVENT_QUIC_FRAMES_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c,
ngx_quic_frame_t *frame, void *data);
ngx_quic_frame_t *ngx_quic_alloc_frame(ngx_connection_t *c);
void ngx_quic_free_frame(ngx_connection_t *c, ngx_quic_frame_t *frame);
void ngx_quic_free_frames(ngx_connection_t *c, ngx_queue_t *frames);
void ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame);
ngx_int_t ngx_quic_split_frame(ngx_connection_t *c, ngx_quic_frame_t *f,
size_t len);
ngx_chain_t *ngx_quic_alloc_chain(ngx_connection_t *c);
void ngx_quic_free_chain(ngx_connection_t *c, ngx_chain_t *in);
ngx_chain_t *ngx_quic_copy_buffer(ngx_connection_t *c, u_char *data,
size_t len);
ngx_chain_t *ngx_quic_read_buffer(ngx_connection_t *c, ngx_quic_buffer_t *qb,
uint64_t limit);
ngx_chain_t *ngx_quic_write_buffer(ngx_connection_t *c, ngx_quic_buffer_t *qb,
ngx_chain_t *in, uint64_t limit, uint64_t offset);
void ngx_quic_skip_buffer(ngx_connection_t *c, ngx_quic_buffer_t *qb,
uint64_t offset);
void ngx_quic_free_buffer(ngx_connection_t *c, ngx_quic_buffer_t *qb);
#if (NGX_DEBUG)
void ngx_quic_log_frame(ngx_log_t *log, ngx_quic_frame_t *f, ngx_uint_t tx);
#else
#define ngx_quic_log_frame(log, f, tx)
#endif
#endif /* _NGX_EVENT_QUIC_FRAMES_H_INCLUDED_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_MIGRATION_H_INCLUDED_
#define _NGX_EVENT_QUIC_MIGRATION_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#define NGX_QUIC_PATH_RETRIES 3
#define NGX_QUIC_PATH_PROBE 0
#define NGX_QUIC_PATH_ACTIVE 1
#define NGX_QUIC_PATH_BACKUP 2
#define ngx_quic_path_dbg(c, msg, path) \
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, \
"quic path seq:%uL %s tx:%O rx:%O valid:%d st:%d mtu:%uz", \
path->seqnum, msg, path->sent, path->received, \
path->validated, path->state, path->mtu);
ngx_int_t ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f);
ngx_int_t ngx_quic_handle_path_response_frame(ngx_connection_t *c,
ngx_quic_path_challenge_frame_t *f);
ngx_quic_path_t *ngx_quic_new_path(ngx_connection_t *c,
struct sockaddr *sockaddr, socklen_t socklen, ngx_quic_client_id_t *cid);
ngx_int_t ngx_quic_free_path(ngx_connection_t *c, ngx_quic_path_t *path);
ngx_int_t ngx_quic_set_path(ngx_connection_t *c, ngx_quic_header_t *pkt);
ngx_int_t ngx_quic_handle_migration(ngx_connection_t *c,
ngx_quic_header_t *pkt);
void ngx_quic_path_handler(ngx_event_t *ev);
void ngx_quic_discover_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path);
ngx_int_t ngx_quic_handle_path_mtu(ngx_connection_t *c,
ngx_quic_path_t *path, uint64_t min, uint64_t max);
#endif /* _NGX_EVENT_QUIC_MIGRATION_H_INCLUDED_ */

View file

@ -0,0 +1,651 @@
/*
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_quic_connection.h>
#if (NGX_QUIC_OPENSSL_COMPAT)
#define NGX_QUIC_COMPAT_RECORD_SIZE 1024
#define NGX_QUIC_COMPAT_SSL_TP_EXT 0x39
#define NGX_QUIC_COMPAT_CLIENT_HANDSHAKE "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
#define NGX_QUIC_COMPAT_SERVER_HANDSHAKE "SERVER_HANDSHAKE_TRAFFIC_SECRET"
#define NGX_QUIC_COMPAT_CLIENT_APPLICATION "CLIENT_TRAFFIC_SECRET_0"
#define NGX_QUIC_COMPAT_SERVER_APPLICATION "SERVER_TRAFFIC_SECRET_0"
typedef struct {
ngx_quic_secret_t secret;
ngx_uint_t cipher;
} ngx_quic_compat_keys_t;
typedef struct {
ngx_log_t *log;
u_char type;
ngx_str_t payload;
uint64_t number;
ngx_quic_compat_keys_t *keys;
enum ssl_encryption_level_t level;
} ngx_quic_compat_record_t;
struct ngx_quic_compat_s {
const SSL_QUIC_METHOD *method;
enum ssl_encryption_level_t write_level;
uint64_t read_record;
ngx_quic_compat_keys_t keys;
ngx_str_t tp;
ngx_str_t ctp;
};
static void ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line);
static ngx_int_t ngx_quic_compat_set_encryption_secret(ngx_connection_t *c,
ngx_quic_compat_keys_t *keys, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);
static void ngx_quic_compat_cleanup_encryption_secret(void *data);
static int ngx_quic_compat_add_transport_params_callback(SSL *ssl,
unsigned int ext_type, unsigned int context, const unsigned char **out,
size_t *outlen, X509 *x, size_t chainidx, int *al, void *add_arg);
static int ngx_quic_compat_parse_transport_params_callback(SSL *ssl,
unsigned int ext_type, unsigned int context, const unsigned char *in,
size_t inlen, X509 *x, size_t chainidx, int *al, void *parse_arg);
static void ngx_quic_compat_message_callback(int write_p, int version,
int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
static size_t ngx_quic_compat_create_header(ngx_quic_compat_record_t *rec,
u_char *out, ngx_uint_t plain);
static ngx_int_t ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec,
ngx_str_t *res);
ngx_int_t
ngx_quic_compat_init(ngx_conf_t *cf, SSL_CTX *ctx)
{
SSL_CTX_set_keylog_callback(ctx, ngx_quic_compat_keylog_callback);
if (SSL_CTX_has_client_custom_ext(ctx, NGX_QUIC_COMPAT_SSL_TP_EXT)) {
return NGX_OK;
}
if (SSL_CTX_add_custom_ext(ctx, NGX_QUIC_COMPAT_SSL_TP_EXT,
SSL_EXT_CLIENT_HELLO
|SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
ngx_quic_compat_add_transport_params_callback,
NULL,
NULL,
ngx_quic_compat_parse_transport_params_callback,
NULL)
== 0)
{
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"SSL_CTX_add_custom_ext() failed");
return NGX_ERROR;
}
return NGX_OK;
}
static void
ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line)
{
u_char ch, *p, *start, value;
size_t n;
ngx_uint_t write;
const SSL_CIPHER *cipher;
ngx_quic_compat_t *com;
ngx_connection_t *c;
ngx_quic_connection_t *qc;
enum ssl_encryption_level_t level;
u_char secret[EVP_MAX_MD_SIZE];
c = ngx_ssl_get_connection(ssl);
if (c->type != SOCK_DGRAM) {
return;
}
p = (u_char *) line;
for (start = p; *p && *p != ' '; p++);
n = p - start;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat secret %*s", n, start);
if (n == sizeof(NGX_QUIC_COMPAT_CLIENT_HANDSHAKE) - 1
&& ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_HANDSHAKE, n) == 0)
{
level = ssl_encryption_handshake;
write = 0;
} else if (n == sizeof(NGX_QUIC_COMPAT_SERVER_HANDSHAKE) - 1
&& ngx_strncmp(start, NGX_QUIC_COMPAT_SERVER_HANDSHAKE, n) == 0)
{
level = ssl_encryption_handshake;
write = 1;
} else if (n == sizeof(NGX_QUIC_COMPAT_CLIENT_APPLICATION) - 1
&& ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_APPLICATION, n)
== 0)
{
level = ssl_encryption_application;
write = 0;
} else if (n == sizeof(NGX_QUIC_COMPAT_SERVER_APPLICATION) - 1
&& ngx_strncmp(start, NGX_QUIC_COMPAT_SERVER_APPLICATION, n)
== 0)
{
level = ssl_encryption_application;
write = 1;
} else {
return;
}
if (*p++ == '\0') {
return;
}
for ( /* void */ ; *p && *p != ' '; p++);
if (*p++ == '\0') {
return;
}
for (n = 0, start = p; *p; p++) {
ch = *p;
if (ch >= '0' && ch <= '9') {
value = ch - '0';
goto next;
}
ch = (u_char) (ch | 0x20);
if (ch >= 'a' && ch <= 'f') {
value = ch - 'a' + 10;
goto next;
}
ngx_log_error(NGX_LOG_EMERG, c->log, 0,
"invalid OpenSSL QUIC secret format");
return;
next:
if ((p - start) % 2) {
secret[n++] += value;
} else {
if (n >= EVP_MAX_MD_SIZE) {
ngx_log_error(NGX_LOG_EMERG, c->log, 0,
"too big OpenSSL QUIC secret");
return;
}
secret[n] = (value << 4);
}
}
qc = ngx_quic_get_connection(c);
com = qc->compat;
cipher = SSL_get_current_cipher(ssl);
if (write) {
com->method->set_write_secret((SSL *) ssl, level, cipher, secret, n);
com->write_level = level;
} else {
com->method->set_read_secret((SSL *) ssl, level, cipher, secret, n);
com->read_record = 0;
(void) ngx_quic_compat_set_encryption_secret(c, &com->keys, level,
cipher, secret, n);
}
ngx_explicit_memzero(secret, n);
}
static ngx_int_t
ngx_quic_compat_set_encryption_secret(ngx_connection_t *c,
ngx_quic_compat_keys_t *keys, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
{
ngx_int_t key_len;
ngx_str_t secret_str;
ngx_uint_t i;
ngx_quic_md_t key;
ngx_quic_hkdf_t seq[2];
ngx_quic_secret_t *peer_secret;
ngx_quic_ciphers_t ciphers;
ngx_pool_cleanup_t *cln;
peer_secret = &keys->secret;
keys->cipher = SSL_CIPHER_get_id(cipher);
key_len = ngx_quic_ciphers(keys->cipher, &ciphers);
if (key_len == NGX_ERROR) {
ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "unexpected cipher");
return NGX_ERROR;
}
key.len = key_len;
peer_secret->iv.len = NGX_QUIC_IV_LEN;
secret_str.len = secret_len;
secret_str.data = (u_char *) secret;
ngx_quic_hkdf_set(&seq[0], "tls13 key", &key, &secret_str);
ngx_quic_hkdf_set(&seq[1], "tls13 iv", &peer_secret->iv, &secret_str);
for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {
if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, c->log) != NGX_OK) {
return NGX_ERROR;
}
}
/* register cleanup handler once */
if (peer_secret->ctx) {
ngx_quic_crypto_cleanup(peer_secret);
} else {
cln = ngx_pool_cleanup_add(c->pool, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_quic_compat_cleanup_encryption_secret;
cln->data = peer_secret;
}
if (ngx_quic_crypto_init(ciphers.c, peer_secret, &key, 1, c->log)
== NGX_ERROR)
{
return NGX_ERROR;
}
ngx_explicit_memzero(key.data, key.len);
return NGX_OK;
}
static void
ngx_quic_compat_cleanup_encryption_secret(void *data)
{
ngx_quic_secret_t *secret = data;
ngx_quic_crypto_cleanup(secret);
}
static int
ngx_quic_compat_add_transport_params_callback(SSL *ssl, unsigned int ext_type,
unsigned int context, const unsigned char **out, size_t *outlen, X509 *x,
size_t chainidx, int *al, void *add_arg)
{
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
if (c->type != SOCK_DGRAM) {
return 0;
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat add transport params");
qc = ngx_quic_get_connection(c);
com = qc->compat;
*out = com->tp.data;
*outlen = com->tp.len;
return 1;
}
static int
ngx_quic_compat_parse_transport_params_callback(SSL *ssl, unsigned int ext_type,
unsigned int context, const unsigned char *in, size_t inlen, X509 *x,
size_t chainidx, int *al, void *parse_arg)
{
u_char *p;
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
if (c->type != SOCK_DGRAM) {
return 0;
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat parse transport params");
qc = ngx_quic_get_connection(c);
com = qc->compat;
p = ngx_pnalloc(c->pool, inlen);
if (p == NULL) {
return 0;
}
ngx_memcpy(p, in, inlen);
com->ctp.data = p;
com->ctp.len = inlen;
return 1;
}
int
SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method)
{
BIO *rbio, *wbio;
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat set method");
qc = ngx_quic_get_connection(c);
qc->compat = ngx_pcalloc(c->pool, sizeof(ngx_quic_compat_t));
if (qc->compat == NULL) {
return 0;
}
com = qc->compat;
com->method = quic_method;
rbio = BIO_new(BIO_s_mem());
if (rbio == NULL) {
return 0;
}
wbio = BIO_new(BIO_s_null());
if (wbio == NULL) {
return 0;
}
SSL_set_bio(ssl, rbio, wbio);
SSL_set_msg_callback(ssl, ngx_quic_compat_message_callback);
/* early data is not supported */
SSL_set_max_early_data(ssl, 0);
return 1;
}
static void
ngx_quic_compat_message_callback(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl, void *arg)
{
ngx_uint_t alert;
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
enum ssl_encryption_level_t level;
if (!write_p) {
return;
}
c = ngx_ssl_get_connection(ssl);
qc = ngx_quic_get_connection(c);
if (qc == NULL) {
/* closing */
return;
}
com = qc->compat;
level = com->write_level;
switch (content_type) {
case SSL3_RT_HANDSHAKE:
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat tx %s len:%uz ",
ngx_quic_level_name(level), len);
if (com->method->add_handshake_data(ssl, level, buf, len) != 1) {
goto failed;
}
break;
case SSL3_RT_ALERT:
if (len >= 2) {
alert = ((u_char *) buf)[1];
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat %s alert:%ui len:%uz ",
ngx_quic_level_name(level), alert, len);
if (com->method->send_alert(ssl, level, alert) != 1) {
goto failed;
}
}
break;
}
return;
failed:
ngx_post_event(&qc->close, &ngx_posted_events);
}
int
SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len)
{
BIO *rbio;
size_t n;
u_char *p;
ngx_str_t res;
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
ngx_quic_compat_record_t rec;
u_char in[NGX_QUIC_COMPAT_RECORD_SIZE + 1];
u_char out[NGX_QUIC_COMPAT_RECORD_SIZE + 1
+ SSL3_RT_HEADER_LENGTH
+ NGX_QUIC_TAG_LEN];
c = ngx_ssl_get_connection(ssl);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat rx %s len:%uz",
ngx_quic_level_name(level), len);
qc = ngx_quic_get_connection(c);
com = qc->compat;
rbio = SSL_get_rbio(ssl);
while (len) {
ngx_memzero(&rec, sizeof(ngx_quic_compat_record_t));
rec.type = SSL3_RT_HANDSHAKE;
rec.log = c->log;
rec.number = com->read_record++;
rec.keys = &com->keys;
rec.level = level;
if (level == ssl_encryption_initial) {
n = ngx_min(len, 65535);
rec.payload.len = n;
rec.payload.data = (u_char *) data;
ngx_quic_compat_create_header(&rec, out, 1);
BIO_write(rbio, out, SSL3_RT_HEADER_LENGTH);
BIO_write(rbio, data, n);
#if defined(NGX_QUIC_DEBUG_CRYPTO) && defined(NGX_QUIC_DEBUG_PACKETS)
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat record len:%uz %*xs%*xs",
n + SSL3_RT_HEADER_LENGTH,
(size_t) SSL3_RT_HEADER_LENGTH, out, n, data);
#endif
} else {
n = ngx_min(len, NGX_QUIC_COMPAT_RECORD_SIZE);
p = ngx_cpymem(in, data, n);
*p++ = SSL3_RT_HANDSHAKE;
rec.payload.len = p - in;
rec.payload.data = in;
res.data = out;
if (ngx_quic_compat_create_record(&rec, &res) != NGX_OK) {
return 0;
}
#if defined(NGX_QUIC_DEBUG_CRYPTO) && defined(NGX_QUIC_DEBUG_PACKETS)
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat record len:%uz %xV", res.len, &res);
#endif
BIO_write(rbio, res.data, res.len);
}
data += n;
len -= n;
}
return 1;
}
static size_t
ngx_quic_compat_create_header(ngx_quic_compat_record_t *rec, u_char *out,
ngx_uint_t plain)
{
u_char type;
size_t len;
len = rec->payload.len;
if (plain) {
type = rec->type;
} else {
type = SSL3_RT_APPLICATION_DATA;
len += NGX_QUIC_TAG_LEN;
}
out[0] = type;
out[1] = 0x03;
out[2] = 0x03;
out[3] = (len >> 8);
out[4] = len;
return 5;
}
static ngx_int_t
ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec, ngx_str_t *res)
{
ngx_str_t ad, out;
ngx_quic_secret_t *secret;
u_char nonce[NGX_QUIC_IV_LEN];
ad.data = res->data;
ad.len = ngx_quic_compat_create_header(rec, ad.data, 0);
out.len = rec->payload.len + NGX_QUIC_TAG_LEN;
out.data = res->data + ad.len;
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, rec->log, 0,
"quic compat ad len:%uz %xV", ad.len, &ad);
#endif
secret = &rec->keys->secret;
ngx_memcpy(nonce, secret->iv.data, secret->iv.len);
ngx_quic_compute_nonce(nonce, sizeof(nonce), rec->number);
if (ngx_quic_crypto_seal(secret, &out, nonce, &rec->payload, &ad, rec->log)
!= NGX_OK)
{
return NGX_ERROR;
}
res->len = ad.len + out.len;
return NGX_OK;
}
int
SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
size_t params_len)
{
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
qc = ngx_quic_get_connection(c);
com = qc->compat;
com->tp.len = params_len;
com->tp.data = (u_char *) params;
return 1;
}
void
SSL_get_peer_quic_transport_params(const SSL *ssl, const uint8_t **out_params,
size_t *out_params_len)
{
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
qc = ngx_quic_get_connection(c);
com = qc->compat;
*out_params = com->ctp.data;
*out_params_len = com->ctp.len;
}
#endif /* NGX_QUIC_OPENSSL_COMPAT */

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
#define _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
#if defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION \
|| defined LIBRESSL_VERSION_NUMBER
#undef NGX_QUIC_OPENSSL_COMPAT
#else
#include <ngx_config.h>
#include <ngx_core.h>
typedef struct ngx_quic_compat_s ngx_quic_compat_t;
enum ssl_encryption_level_t {
ssl_encryption_initial = 0,
ssl_encryption_early_data,
ssl_encryption_handshake,
ssl_encryption_application
};
typedef struct ssl_quic_method_st {
int (*set_read_secret)(SSL *ssl, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher,
const uint8_t *rsecret, size_t secret_len);
int (*set_write_secret)(SSL *ssl, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher,
const uint8_t *wsecret, size_t secret_len);
int (*add_handshake_data)(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len);
int (*flush_flight)(SSL *ssl);
int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level,
uint8_t alert);
} SSL_QUIC_METHOD;
ngx_int_t ngx_quic_compat_init(ngx_conf_t *cf, SSL_CTX *ctx);
int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method);
int SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len);
int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
size_t params_len);
void SSL_get_peer_quic_transport_params(const SSL *ssl,
const uint8_t **out_params, size_t *out_params_len);
#endif /* TLSEXT_TYPE_quic_transport_parameters */
#endif /* _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_OUTPUT_H_INCLUDED_
#define _NGX_EVENT_QUIC_OUTPUT_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
ngx_int_t ngx_quic_output(ngx_connection_t *c);
ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c,
ngx_quic_header_t *inpkt);
ngx_int_t ngx_quic_send_stateless_reset(ngx_connection_t *c,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
ngx_int_t ngx_quic_send_cc(ngx_connection_t *c);
ngx_int_t ngx_quic_send_early_cc(ngx_connection_t *c,
ngx_quic_header_t *inpkt, ngx_uint_t err, const char *reason);
ngx_int_t ngx_quic_send_retry(ngx_connection_t *c,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
ngx_int_t ngx_quic_send_new_token(ngx_connection_t *c, ngx_quic_path_t *path);
ngx_int_t ngx_quic_send_ack(ngx_connection_t *c,
ngx_quic_send_ctx_t *ctx);
ngx_int_t ngx_quic_send_ack_range(ngx_connection_t *c,
ngx_quic_send_ctx_t *ctx, uint64_t smallest, uint64_t largest);
ngx_int_t ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame,
size_t min, ngx_quic_path_t *path);
size_t ngx_quic_path_limit(ngx_connection_t *c, ngx_quic_path_t *path,
size_t size);
#endif /* _NGX_EVENT_QUIC_OUTPUT_H_INCLUDED_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,120 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_
#define _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event_quic_transport.h>
#define NGX_QUIC_ENCRYPTION_LAST ((ssl_encryption_application) + 1)
/* RFC 5116, 5.1/5.3 and RFC 8439, 2.3/2.5 for all supported ciphers */
#define NGX_QUIC_IV_LEN 12
#define NGX_QUIC_TAG_LEN 16
/* largest hash used in TLS is SHA-384 */
#define NGX_QUIC_MAX_MD_SIZE 48
#ifdef OPENSSL_IS_BORINGSSL
#define ngx_quic_cipher_t EVP_AEAD
#define ngx_quic_crypto_ctx_t EVP_AEAD_CTX
#else
#define ngx_quic_cipher_t EVP_CIPHER
#define ngx_quic_crypto_ctx_t EVP_CIPHER_CTX
#endif
typedef struct {
size_t len;
u_char data[NGX_QUIC_MAX_MD_SIZE];
} ngx_quic_md_t;
typedef struct {
size_t len;
u_char data[NGX_QUIC_IV_LEN];
} ngx_quic_iv_t;
typedef struct {
ngx_quic_md_t secret;
ngx_quic_iv_t iv;
ngx_quic_md_t hp;
ngx_quic_crypto_ctx_t *ctx;
EVP_CIPHER_CTX *hp_ctx;
} ngx_quic_secret_t;
typedef struct {
ngx_quic_secret_t client;
ngx_quic_secret_t server;
} ngx_quic_secrets_t;
struct ngx_quic_keys_s {
ngx_quic_secrets_t secrets[NGX_QUIC_ENCRYPTION_LAST];
ngx_quic_secrets_t next_key;
ngx_uint_t cipher;
};
typedef struct {
const ngx_quic_cipher_t *c;
const EVP_CIPHER *hp;
const EVP_MD *d;
} ngx_quic_ciphers_t;
typedef struct {
size_t out_len;
u_char *out;
size_t prk_len;
const uint8_t *prk;
size_t label_len;
const u_char *label;
} ngx_quic_hkdf_t;
#define ngx_quic_hkdf_set(seq, _label, _out, _prk) \
(seq)->out_len = (_out)->len; (seq)->out = (_out)->data; \
(seq)->prk_len = (_prk)->len, (seq)->prk = (_prk)->data, \
(seq)->label_len = (sizeof(_label) - 1); (seq)->label = (u_char *)(_label);
ngx_int_t ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys,
ngx_str_t *secret, ngx_log_t *log);
ngx_int_t ngx_quic_keys_set_encryption_secret(ngx_log_t *log,
ngx_uint_t is_write, ngx_quic_keys_t *keys,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len);
ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys,
enum ssl_encryption_level_t level, ngx_uint_t is_write);
void ngx_quic_keys_discard(ngx_quic_keys_t *keys,
enum ssl_encryption_level_t level);
void ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys);
void ngx_quic_keys_update(ngx_event_t *ev);
void ngx_quic_keys_cleanup(ngx_quic_keys_t *keys);
ngx_int_t ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_str_t *res);
ngx_int_t ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn);
void ngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn);
ngx_int_t ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers);
ngx_int_t ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher,
ngx_quic_secret_t *s, ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log);
ngx_int_t ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out,
u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
void ngx_quic_crypto_cleanup(ngx_quic_secret_t *s);
ngx_int_t ngx_quic_hkdf_expand(ngx_quic_hkdf_t *hkdf, const EVP_MD *digest,
ngx_log_t *log);
#endif /* _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_ */

View file

@ -0,0 +1,237 @@
/*
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_quic_connection.h>
ngx_int_t
ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,
ngx_quic_header_t *pkt)
{
ngx_quic_socket_t *qsock, *tmp;
ngx_quic_client_id_t *cid;
/*
* qc->path = NULL
*
* qc->nclient_ids = 0
* qc->nsockets = 0
* qc->max_retired_seqnum = 0
* qc->client_seqnum = 0
*/
ngx_queue_init(&qc->sockets);
ngx_queue_init(&qc->free_sockets);
ngx_queue_init(&qc->paths);
ngx_queue_init(&qc->free_paths);
ngx_queue_init(&qc->client_ids);
ngx_queue_init(&qc->free_client_ids);
qc->tp.original_dcid.len = pkt->odcid.len;
qc->tp.original_dcid.data = ngx_pstrdup(c->pool, &pkt->odcid);
if (qc->tp.original_dcid.data == NULL) {
return NGX_ERROR;
}
/* socket to use for further processing (id auto-generated) */
qsock = ngx_quic_create_socket(c, qc);
if (qsock == NULL) {
return NGX_ERROR;
}
/* socket is listening at new server id */
if (ngx_quic_listen(c, qc, qsock) != NGX_OK) {
return NGX_ERROR;
}
qsock->used = 1;
qc->tp.initial_scid.len = qsock->sid.len;
qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len);
if (qc->tp.initial_scid.data == NULL) {
goto failed;
}
ngx_memcpy(qc->tp.initial_scid.data, qsock->sid.id, qsock->sid.len);
/* for all packets except first, this is set at udp layer */
c->udp = &qsock->udp;
/* ngx_quic_get_connection(c) macro is now usable */
/* we have a client identified by scid */
cid = ngx_quic_create_client_id(c, &pkt->scid, 0, NULL);
if (cid == NULL) {
goto failed;
}
/* path of the first packet is our initial active path */
qc->path = ngx_quic_new_path(c, c->sockaddr, c->socklen, cid);
if (qc->path == NULL) {
goto failed;
}
qc->path->tag = NGX_QUIC_PATH_ACTIVE;
if (pkt->validated) {
qc->path->validated = 1;
}
ngx_quic_path_dbg(c, "set active", qc->path);
tmp = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t));
if (tmp == NULL) {
goto failed;
}
tmp->sid.seqnum = NGX_QUIC_UNSET_PN; /* temporary socket */
ngx_memcpy(tmp->sid.id, pkt->dcid.data, pkt->dcid.len);
tmp->sid.len = pkt->dcid.len;
if (ngx_quic_listen(c, qc, tmp) != NGX_OK) {
goto failed;
}
return NGX_OK;
failed:
ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node);
c->udp = NULL;
return NGX_ERROR;
}
ngx_quic_socket_t *
ngx_quic_create_socket(ngx_connection_t *c, ngx_quic_connection_t *qc)
{
ngx_queue_t *q;
ngx_quic_socket_t *sock;
if (!ngx_queue_empty(&qc->free_sockets)) {
q = ngx_queue_head(&qc->free_sockets);
sock = ngx_queue_data(q, ngx_quic_socket_t, queue);
ngx_queue_remove(&sock->queue);
ngx_memzero(sock, sizeof(ngx_quic_socket_t));
} else {
sock = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t));
if (sock == NULL) {
return NULL;
}
}
sock->sid.len = NGX_QUIC_SERVER_CID_LEN;
if (ngx_quic_create_server_id(c, sock->sid.id) != NGX_OK) {
return NULL;
}
sock->sid.seqnum = qc->server_seqnum++;
return sock;
}
void
ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock)
{
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
ngx_queue_remove(&qsock->queue);
ngx_queue_insert_head(&qc->free_sockets, &qsock->queue);
ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node);
qc->nsockets--;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic socket seq:%L closed nsock:%ui",
(int64_t) qsock->sid.seqnum, qc->nsockets);
}
ngx_int_t
ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,
ngx_quic_socket_t *qsock)
{
ngx_str_t id;
ngx_quic_server_id_t *sid;
sid = &qsock->sid;
id.data = sid->id;
id.len = sid->len;
qsock->udp.connection = c;
qsock->udp.node.key = ngx_crc32_long(id.data, id.len);
qsock->udp.key = id;
ngx_rbtree_insert(&c->listening->rbtree, &qsock->udp.node);
ngx_queue_insert_tail(&qc->sockets, &qsock->queue);
qc->nsockets++;
qsock->quic = qc;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic socket seq:%L listening at sid:%xV nsock:%ui",
(int64_t) sid->seqnum, &id, qc->nsockets);
return NGX_OK;
}
void
ngx_quic_close_sockets(ngx_connection_t *c)
{
ngx_queue_t *q;
ngx_quic_socket_t *qsock;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
while (!ngx_queue_empty(&qc->sockets)) {
q = ngx_queue_head(&qc->sockets);
qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);
ngx_quic_close_socket(c, qsock);
}
}
ngx_quic_socket_t *
ngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum)
{
ngx_queue_t *q;
ngx_quic_socket_t *qsock;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
for (q = ngx_queue_head(&qc->sockets);
q != ngx_queue_sentinel(&qc->sockets);
q = ngx_queue_next(q))
{
qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);
if (qsock->sid.seqnum == seqnum) {
return qsock;
}
}
return NULL;
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_SOCKET_H_INCLUDED_
#define _NGX_EVENT_QUIC_SOCKET_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
ngx_int_t ngx_quic_open_sockets(ngx_connection_t *c,
ngx_quic_connection_t *qc, ngx_quic_header_t *pkt);
void ngx_quic_close_sockets(ngx_connection_t *c);
ngx_quic_socket_t *ngx_quic_create_socket(ngx_connection_t *c,
ngx_quic_connection_t *qc);
ngx_int_t ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,
ngx_quic_socket_t *qsock);
void ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock);
ngx_quic_socket_t *ngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum);
#endif /* _NGX_EVENT_QUIC_SOCKET_H_INCLUDED_ */

View file

@ -0,0 +1,590 @@
/*
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_quic_connection.h>
#if defined OPENSSL_IS_BORINGSSL \
|| defined LIBRESSL_VERSION_NUMBER \
|| NGX_QUIC_OPENSSL_COMPAT
#define NGX_QUIC_BORINGSSL_API 1
#endif
/*
* RFC 9000, 7.5. Cryptographic Message Buffering
*
* Implementations MUST support buffering at least 4096 bytes of data
*/
#define NGX_QUIC_MAX_BUFFERED 65535
#if (NGX_QUIC_BORINGSSL_API)
static int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len);
static int ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len);
#else
static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *read_secret,
const uint8_t *write_secret, size_t secret_len);
#endif
static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *data, size_t len);
static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, uint8_t alert);
static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
enum ssl_encryption_level_t level);
#if (NGX_QUIC_BORINGSSL_API)
static int
ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
const uint8_t *rsecret, size_t secret_len)
{
ngx_connection_t *c;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
qc = ngx_quic_get_connection(c);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_set_read_secret() level:%d", level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic read secret len:%uz %*xs", secret_len,
secret_len, rsecret);
#endif
if (ngx_quic_keys_set_encryption_secret(c->log, 0, qc->keys, level,
cipher, rsecret, secret_len)
!= NGX_OK)
{
return 0;
}
return 1;
}
static int
ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
const uint8_t *wsecret, size_t secret_len)
{
ngx_connection_t *c;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
qc = ngx_quic_get_connection(c);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_set_write_secret() level:%d", level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic write secret len:%uz %*xs", secret_len,
secret_len, wsecret);
#endif
if (ngx_quic_keys_set_encryption_secret(c->log, 1, qc->keys, level,
cipher, wsecret, secret_len)
!= NGX_OK)
{
return 0;
}
return 1;
}
#else
static int
ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *rsecret,
const uint8_t *wsecret, size_t secret_len)
{
ngx_connection_t *c;
const SSL_CIPHER *cipher;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
qc = ngx_quic_get_connection(c);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_set_encryption_secrets() level:%d", level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic read secret len:%uz %*xs", secret_len,
secret_len, rsecret);
#endif
cipher = SSL_get_current_cipher(ssl_conn);
if (ngx_quic_keys_set_encryption_secret(c->log, 0, qc->keys, level,
cipher, rsecret, secret_len)
!= NGX_OK)
{
return 0;
}
if (level == ssl_encryption_early_data) {
return 1;
}
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic write secret len:%uz %*xs", secret_len,
secret_len, wsecret);
#endif
if (ngx_quic_keys_set_encryption_secret(c->log, 1, qc->keys, level,
cipher, wsecret, secret_len)
!= NGX_OK)
{
return 0;
}
return 1;
}
#endif
static int
ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *data, size_t len)
{
u_char *p, *end;
size_t client_params_len;
ngx_chain_t *out;
const uint8_t *client_params;
ngx_quic_tp_t ctp;
ngx_quic_frame_t *frame;
ngx_connection_t *c;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
unsigned int alpn_len;
const unsigned char *alpn_data;
#endif
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
qc = ngx_quic_get_connection(c);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_add_handshake_data");
if (!qc->client_tp_done) {
/*
* things to do once during handshake: check ALPN and transport
* parameters; we want to break handshake if something is wrong
* here;
*/
#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);
if (alpn_len == 0) {
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
qc->error_reason = "unsupported protocol in ALPN extension";
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic unsupported protocol in ALPN extension");
return 0;
}
#endif
SSL_get_peer_quic_transport_params(ssl_conn, &client_params,
&client_params_len);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic SSL_get_peer_quic_transport_params():"
" params_len:%ui", client_params_len);
if (client_params_len == 0) {
/* RFC 9001, 8.2. QUIC Transport Parameters Extension */
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
qc->error_reason = "missing transport parameters";
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"missing transport parameters");
return 0;
}
p = (u_char *) client_params;
end = p + client_params_len;
/* defaults for parameters not sent by client */
ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t));
if (ngx_quic_parse_transport_params(p, end, &ctp, c->log)
!= NGX_OK)
{
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
qc->error_reason = "failed to process transport parameters";
return 0;
}
if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
return 0;
}
qc->client_tp_done = 1;
}
ctx = ngx_quic_get_send_ctx(qc, level);
out = ngx_quic_copy_buffer(c, (u_char *) data, len);
if (out == NGX_CHAIN_ERROR) {
return 0;
}
frame = ngx_quic_alloc_frame(c);
if (frame == NULL) {
return 0;
}
frame->data = out;
frame->level = level;
frame->type = NGX_QUIC_FT_CRYPTO;
frame->u.crypto.offset = ctx->crypto_sent;
frame->u.crypto.length = len;
ctx->crypto_sent += len;
ngx_quic_queue_frame(qc, frame);
return 1;
}
static int
ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
{
#if (NGX_DEBUG)
ngx_connection_t *c;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_flush_flight()");
#endif
return 1;
}
static int
ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
uint8_t alert)
{
ngx_connection_t *c;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_send_alert() level:%s alert:%d",
ngx_quic_level_name(level), (int) alert);
/* already closed on regular shutdown */
qc = ngx_quic_get_connection(c);
if (qc == NULL) {
return 1;
}
qc->error = NGX_QUIC_ERR_CRYPTO(alert);
qc->error_reason = "handshake failed";
return 1;
}
ngx_int_t
ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
ngx_quic_frame_t *frame)
{
uint64_t last;
ngx_chain_t *cl;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
ngx_quic_crypto_frame_t *f;
qc = ngx_quic_get_connection(c);
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
f = &frame->u.crypto;
/* no overflow since both values are 62-bit */
last = f->offset + f->length;
if (last > ctx->crypto.offset + NGX_QUIC_MAX_BUFFERED) {
qc->error = NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED;
return NGX_ERROR;
}
if (last <= ctx->crypto.offset) {
if (pkt->level == ssl_encryption_initial) {
/* speeding up handshake completion */
if (!ngx_queue_empty(&ctx->sent)) {
ngx_quic_resend_frames(c, ctx);
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
while (!ngx_queue_empty(&ctx->sent)) {
ngx_quic_resend_frames(c, ctx);
}
}
}
return NGX_OK;
}
if (f->offset == ctx->crypto.offset) {
if (ngx_quic_crypto_input(c, frame->data, pkt->level) != NGX_OK) {
return NGX_ERROR;
}
ngx_quic_skip_buffer(c, &ctx->crypto, last);
} else {
if (ngx_quic_write_buffer(c, &ctx->crypto, frame->data, f->length,
f->offset)
== NGX_CHAIN_ERROR)
{
return NGX_ERROR;
}
}
cl = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1);
if (cl) {
if (ngx_quic_crypto_input(c, cl, pkt->level) != NGX_OK) {
return NGX_ERROR;
}
ngx_quic_free_chain(c, cl);
}
return NGX_OK;
}
static ngx_int_t
ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
enum ssl_encryption_level_t level)
{
int n, sslerr;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_ssl_conn_t *ssl_conn;
ngx_quic_frame_t *frame;
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
ssl_conn = c->ssl->connection;
for (cl = data; cl; cl = cl->next) {
b = cl->buf;
if (!SSL_provide_quic_data(ssl_conn, level, b->pos, b->last - b->pos)) {
ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
"SSL_provide_quic_data() failed");
return NGX_ERROR;
}
}
n = SSL_do_handshake(ssl_conn);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
if (n <= 0) {
sslerr = SSL_get_error(ssl_conn, n);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d",
sslerr);
if (sslerr != SSL_ERROR_WANT_READ) {
if (c->ssl->handshake_rejected) {
ngx_connection_error(c, 0, "handshake rejected");
ERR_clear_error();
return NGX_ERROR;
}
ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed");
return NGX_ERROR;
}
}
if (n <= 0 || SSL_in_init(ssl_conn)) {
if (ngx_quic_keys_available(qc->keys, ssl_encryption_early_data, 0)
&& qc->client_tp_done)
{
if (ngx_quic_init_streams(c) != NGX_OK) {
return NGX_ERROR;
}
}
return NGX_OK;
}
#if (NGX_DEBUG)
ngx_ssl_handshake_log(c);
#endif
c->ssl->handshaked = 1;
frame = ngx_quic_alloc_frame(c);
if (frame == NULL) {
return NGX_ERROR;
}
frame->level = ssl_encryption_application;
frame->type = NGX_QUIC_FT_HANDSHAKE_DONE;
ngx_quic_queue_frame(qc, frame);
if (qc->conf->retry) {
if (ngx_quic_send_new_token(c, qc->path) != NGX_OK) {
return NGX_ERROR;
}
}
/*
* RFC 9001, 9.5. Header Protection Timing Side Channels
*
* Generating next keys before a key update is received.
*/
ngx_post_event(&qc->key_update, &ngx_posted_events);
/*
* RFC 9001, 4.9.2. Discarding Handshake Keys
*
* An endpoint MUST discard its Handshake keys
* when the TLS handshake is confirmed.
*/
ngx_quic_discard_ctx(c, ssl_encryption_handshake);
ngx_quic_discover_path_mtu(c, qc->path);
/* start accepting clients on negotiated number of server ids */
if (ngx_quic_create_sockets(c) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_quic_init_streams(c) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
ngx_int_t
ngx_quic_init_connection(ngx_connection_t *c)
{
u_char *p;
size_t clen;
ssize_t len;
ngx_str_t dcid;
ngx_ssl_conn_t *ssl_conn;
ngx_quic_socket_t *qsock;
ngx_quic_connection_t *qc;
static SSL_QUIC_METHOD quic_method;
qc = ngx_quic_get_connection(c);
if (ngx_ssl_create_connection(qc->conf->ssl, c, 0) != NGX_OK) {
return NGX_ERROR;
}
c->ssl->no_wait_shutdown = 1;
ssl_conn = c->ssl->connection;
if (!quic_method.send_alert) {
#if (NGX_QUIC_BORINGSSL_API)
quic_method.set_read_secret = ngx_quic_set_read_secret;
quic_method.set_write_secret = ngx_quic_set_write_secret;
#else
quic_method.set_encryption_secrets = ngx_quic_set_encryption_secrets;
#endif
quic_method.add_handshake_data = ngx_quic_add_handshake_data;
quic_method.flush_flight = ngx_quic_flush_flight;
quic_method.send_alert = ngx_quic_send_alert;
}
if (SSL_set_quic_method(ssl_conn, &quic_method) == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic SSL_set_quic_method() failed");
return NGX_ERROR;
}
#ifdef OPENSSL_INFO_QUIC
if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
SSL_set_quic_early_data_enabled(ssl_conn, 1);
}
#endif
qsock = ngx_quic_get_socket(c);
dcid.data = qsock->sid.id;
dcid.len = qsock->sid.len;
if (ngx_quic_new_sr_token(c, &dcid, qc->conf->sr_token_key, qc->tp.sr_token)
!= NGX_OK)
{
return NGX_ERROR;
}
len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp, &clen);
/* always succeeds */
p = ngx_pnalloc(c->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
len = ngx_quic_create_transport_params(p, p + len, &qc->tp, NULL);
if (len < 0) {
return NGX_ERROR;
}
#ifdef NGX_QUIC_DEBUG_PACKETS
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic transport parameters len:%uz %*xs", len, len, p);
#endif
if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic SSL_set_quic_transport_params() failed");
return NGX_ERROR;
}
#ifdef OPENSSL_IS_BORINGSSL
if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic SSL_set_quic_early_data_context() failed");
return NGX_ERROR;
}
#endif
return NGX_OK;
}

View file

@ -0,0 +1,19 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_SSL_H_INCLUDED_
#define _NGX_EVENT_QUIC_SSL_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_frame_t *frame);
#endif /* _NGX_EVENT_QUIC_SSL_H_INCLUDED_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_STREAMS_H_INCLUDED_
#define _NGX_EVENT_QUIC_STREAMS_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
ngx_int_t ngx_quic_handle_stream_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_frame_t *frame);
void ngx_quic_handle_stream_ack(ngx_connection_t *c,
ngx_quic_frame_t *f);
ngx_int_t ngx_quic_handle_max_data_frame(ngx_connection_t *c,
ngx_quic_max_data_frame_t *f);
ngx_int_t ngx_quic_handle_streams_blocked_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_streams_blocked_frame_t *f);
ngx_int_t ngx_quic_handle_data_blocked_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_data_blocked_frame_t *f);
ngx_int_t ngx_quic_handle_stream_data_blocked_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_stream_data_blocked_frame_t *f);
ngx_int_t ngx_quic_handle_max_stream_data_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_max_stream_data_frame_t *f);
ngx_int_t ngx_quic_handle_reset_stream_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_reset_stream_frame_t *f);
ngx_int_t ngx_quic_handle_stop_sending_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_stop_sending_frame_t *f);
ngx_int_t ngx_quic_handle_max_streams_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_max_streams_frame_t *f);
ngx_int_t ngx_quic_init_streams(ngx_connection_t *c);
void ngx_quic_rbtree_insert_stream(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
ngx_quic_stream_t *ngx_quic_find_stream(ngx_rbtree_t *rbtree,
uint64_t id);
ngx_int_t ngx_quic_close_streams(ngx_connection_t *c,
ngx_quic_connection_t *qc);
#endif /* _NGX_EVENT_QUIC_STREAMS_H_INCLUDED_ */

View file

@ -0,0 +1,309 @@
/*
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_sha1.h>
#include <ngx_event_quic_connection.h>
static void ngx_quic_address_hash(struct sockaddr *sockaddr, socklen_t socklen,
ngx_uint_t no_port, u_char buf[20]);
ngx_int_t
ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,
u_char *token)
{
ngx_str_t tmp;
tmp.data = secret;
tmp.len = NGX_QUIC_SR_KEY_LEN;
if (ngx_quic_derive_key(c->log, "sr_token_key", &tmp, cid, token,
NGX_QUIC_SR_TOKEN_LEN)
!= NGX_OK)
{
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic stateless reset token %*xs",
(size_t) NGX_QUIC_SR_TOKEN_LEN, token);
return NGX_OK;
}
ngx_int_t
ngx_quic_new_token(ngx_log_t *log, struct sockaddr *sockaddr,
socklen_t socklen, u_char *key, ngx_str_t *token, ngx_str_t *odcid,
time_t exp, ngx_uint_t is_retry)
{
int len, iv_len;
u_char *p, *iv;
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher;
u_char in[NGX_QUIC_MAX_TOKEN_SIZE];
ngx_quic_address_hash(sockaddr, socklen, !is_retry, in);
p = in + 20;
p = ngx_cpymem(p, &exp, sizeof(time_t));
*p++ = is_retry ? 1 : 0;
if (odcid) {
*p++ = odcid->len;
p = ngx_cpymem(p, odcid->data, odcid->len);
} else {
*p++ = 0;
}
len = p - in;
cipher = EVP_aes_256_gcm();
iv_len = NGX_QUIC_AES_256_GCM_IV_LEN;
if ((size_t) (iv_len + len + NGX_QUIC_AES_256_GCM_TAG_LEN) > token->len) {
ngx_log_error(NGX_LOG_ALERT, log, 0, "quic token buffer is too small");
return NGX_ERROR;
}
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL) {
return NGX_ERROR;
}
iv = token->data;
if (RAND_bytes(iv, iv_len) <= 0
|| !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
{
EVP_CIPHER_CTX_free(ctx);
return NGX_ERROR;
}
token->len = iv_len;
if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) {
EVP_CIPHER_CTX_free(ctx);
return NGX_ERROR;
}
token->len += len;
if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) {
EVP_CIPHER_CTX_free(ctx);
return NGX_ERROR;
}
token->len += len;
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG,
NGX_QUIC_AES_256_GCM_TAG_LEN,
token->data + token->len)
== 0)
{
EVP_CIPHER_CTX_free(ctx);
return NGX_ERROR;
}
token->len += NGX_QUIC_AES_256_GCM_TAG_LEN;
EVP_CIPHER_CTX_free(ctx);
#ifdef NGX_QUIC_DEBUG_PACKETS
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
"quic new token len:%uz %xV", token->len, token);
#endif
return NGX_OK;
}
static void
ngx_quic_address_hash(struct sockaddr *sockaddr, socklen_t socklen,
ngx_uint_t no_port, u_char buf[20])
{
size_t len;
u_char *data;
ngx_sha1_t sha1;
struct sockaddr_in *sin;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
#endif
len = (size_t) socklen;
data = (u_char *) sockaddr;
if (no_port) {
switch (sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) sockaddr;
len = sizeof(struct in6_addr);
data = sin6->sin6_addr.s6_addr;
break;
#endif
case AF_INET:
sin = (struct sockaddr_in *) sockaddr;
len = sizeof(in_addr_t);
data = (u_char *) &sin->sin_addr;
break;
}
}
ngx_sha1_init(&sha1);
ngx_sha1_update(&sha1, data, len);
ngx_sha1_final(buf, &sha1);
}
ngx_int_t
ngx_quic_validate_token(ngx_connection_t *c, u_char *key,
ngx_quic_header_t *pkt)
{
int len, tlen, iv_len;
u_char *iv, *p;
time_t now, exp;
size_t total;
ngx_str_t odcid;
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher;
u_char addr_hash[20];
u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE];
#if NGX_SUPPRESS_WARN
ngx_str_null(&odcid);
#endif
/* Retry token or NEW_TOKEN in a previous connection */
cipher = EVP_aes_256_gcm();
iv = pkt->token.data;
iv_len = NGX_QUIC_AES_256_GCM_IV_LEN;
/* sanity checks */
if (pkt->token.len < (size_t) iv_len + NGX_QUIC_AES_256_GCM_TAG_LEN) {
goto garbage;
}
if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE
+ NGX_QUIC_AES_256_GCM_TAG_LEN)
{
goto garbage;
}
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL) {
return NGX_ERROR;
}
if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
EVP_CIPHER_CTX_free(ctx);
return NGX_ERROR;
}
p = pkt->token.data + iv_len;
len = pkt->token.len - iv_len - NGX_QUIC_AES_256_GCM_TAG_LEN;
if (EVP_DecryptUpdate(ctx, tdec, &tlen, p, len) != 1) {
EVP_CIPHER_CTX_free(ctx);
goto garbage;
}
total = tlen;
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
NGX_QUIC_AES_256_GCM_TAG_LEN, p + len)
== 0)
{
EVP_CIPHER_CTX_free(ctx);
goto garbage;
}
if (EVP_DecryptFinal_ex(ctx, tdec + tlen, &tlen) <= 0) {
EVP_CIPHER_CTX_free(ctx);
goto garbage;
}
total += tlen;
EVP_CIPHER_CTX_free(ctx);
if (total < (20 + sizeof(time_t) + 2)) {
goto garbage;
}
p = tdec + 20;
ngx_memcpy(&exp, p, sizeof(time_t));
p += sizeof(time_t);
pkt->retried = (*p++ == 1);
ngx_quic_address_hash(c->sockaddr, c->socklen, !pkt->retried, addr_hash);
if (ngx_memcmp(tdec, addr_hash, 20) != 0) {
goto bad_token;
}
odcid.len = *p++;
if (odcid.len) {
if (odcid.len > NGX_QUIC_MAX_CID_LEN) {
goto bad_token;
}
if ((size_t)(tdec + total - p) < odcid.len) {
goto bad_token;
}
odcid.data = p;
}
now = ngx_time();
if (now > exp) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
return NGX_DECLINED;
}
if (odcid.len) {
pkt->odcid.len = odcid.len;
pkt->odcid.data = pkt->odcid_buf;
ngx_memcpy(pkt->odcid.data, odcid.data, odcid.len);
} else {
pkt->odcid = pkt->dcid;
}
pkt->validated = 1;
return NGX_OK;
garbage:
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token");
return NGX_ABORT;
bad_token:
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
return NGX_DECLINED;
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_
#define _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#define NGX_QUIC_MAX_TOKEN_SIZE 64
/* SHA-1(addr)=20 + sizeof(time_t) + retry(1) + odcid.len(1) + odcid */
#define NGX_QUIC_AES_256_GCM_IV_LEN 12
#define NGX_QUIC_AES_256_GCM_TAG_LEN 16
#define NGX_QUIC_TOKEN_BUF_SIZE (NGX_QUIC_AES_256_GCM_IV_LEN \
+ NGX_QUIC_MAX_TOKEN_SIZE \
+ NGX_QUIC_AES_256_GCM_TAG_LEN)
ngx_int_t ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid,
u_char *secret, u_char *token);
ngx_int_t ngx_quic_new_token(ngx_log_t *log, struct sockaddr *sockaddr,
socklen_t socklen, u_char *key, ngx_str_t *token, ngx_str_t *odcid,
time_t expires, ngx_uint_t is_retry);
ngx_int_t ngx_quic_validate_token(ngx_connection_t *c,
u_char *key, ngx_quic_header_t *pkt);
#endif /* _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,397 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_TRANSPORT_H_INCLUDED_
#define _NGX_EVENT_QUIC_TRANSPORT_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
/*
* RFC 9000, 17.2. Long Header Packets
* 17.3. Short Header Packets
*
* QUIC flags in first byte
*/
#define NGX_QUIC_PKT_LONG 0x80 /* header form */
#define NGX_QUIC_PKT_FIXED_BIT 0x40
#define NGX_QUIC_PKT_TYPE 0x30 /* in long packet */
#define NGX_QUIC_PKT_KPHASE 0x04 /* in short packet */
#define ngx_quic_long_pkt(flags) ((flags) & NGX_QUIC_PKT_LONG)
#define ngx_quic_short_pkt(flags) (((flags) & NGX_QUIC_PKT_LONG) == 0)
/* Long packet types */
#define NGX_QUIC_PKT_INITIAL 0x00
#define NGX_QUIC_PKT_ZRTT 0x10
#define NGX_QUIC_PKT_HANDSHAKE 0x20
#define NGX_QUIC_PKT_RETRY 0x30
#define ngx_quic_pkt_in(flags) \
(((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_INITIAL)
#define ngx_quic_pkt_zrtt(flags) \
(((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_ZRTT)
#define ngx_quic_pkt_hs(flags) \
(((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_HANDSHAKE)
#define ngx_quic_pkt_retry(flags) \
(((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_RETRY)
#define ngx_quic_pkt_rb_mask(flags) \
(ngx_quic_long_pkt(flags) ? 0x0C : 0x18)
#define ngx_quic_pkt_hp_mask(flags) \
(ngx_quic_long_pkt(flags) ? 0x0F : 0x1F)
#define ngx_quic_level_name(lvl) \
(lvl == ssl_encryption_application) ? "app" \
: (lvl == ssl_encryption_initial) ? "init" \
: (lvl == ssl_encryption_handshake) ? "hs" : "early"
#define NGX_QUIC_MAX_CID_LEN 20
#define NGX_QUIC_SERVER_CID_LEN NGX_QUIC_MAX_CID_LEN
/* 12.4. Frames and Frame Types */
#define NGX_QUIC_FT_PADDING 0x00
#define NGX_QUIC_FT_PING 0x01
#define NGX_QUIC_FT_ACK 0x02
#define NGX_QUIC_FT_ACK_ECN 0x03
#define NGX_QUIC_FT_RESET_STREAM 0x04
#define NGX_QUIC_FT_STOP_SENDING 0x05
#define NGX_QUIC_FT_CRYPTO 0x06
#define NGX_QUIC_FT_NEW_TOKEN 0x07
#define NGX_QUIC_FT_STREAM 0x08
#define NGX_QUIC_FT_STREAM1 0x09
#define NGX_QUIC_FT_STREAM2 0x0A
#define NGX_QUIC_FT_STREAM3 0x0B
#define NGX_QUIC_FT_STREAM4 0x0C
#define NGX_QUIC_FT_STREAM5 0x0D
#define NGX_QUIC_FT_STREAM6 0x0E
#define NGX_QUIC_FT_STREAM7 0x0F
#define NGX_QUIC_FT_MAX_DATA 0x10
#define NGX_QUIC_FT_MAX_STREAM_DATA 0x11
#define NGX_QUIC_FT_MAX_STREAMS 0x12
#define NGX_QUIC_FT_MAX_STREAMS2 0x13
#define NGX_QUIC_FT_DATA_BLOCKED 0x14
#define NGX_QUIC_FT_STREAM_DATA_BLOCKED 0x15
#define NGX_QUIC_FT_STREAMS_BLOCKED 0x16
#define NGX_QUIC_FT_STREAMS_BLOCKED2 0x17
#define NGX_QUIC_FT_NEW_CONNECTION_ID 0x18
#define NGX_QUIC_FT_RETIRE_CONNECTION_ID 0x19
#define NGX_QUIC_FT_PATH_CHALLENGE 0x1A
#define NGX_QUIC_FT_PATH_RESPONSE 0x1B
#define NGX_QUIC_FT_CONNECTION_CLOSE 0x1C
#define NGX_QUIC_FT_CONNECTION_CLOSE_APP 0x1D
#define NGX_QUIC_FT_HANDSHAKE_DONE 0x1E
#define NGX_QUIC_FT_LAST NGX_QUIC_FT_HANDSHAKE_DONE
/* 22.5. QUIC Transport Error Codes Registry */
#define NGX_QUIC_ERR_NO_ERROR 0x00
#define NGX_QUIC_ERR_INTERNAL_ERROR 0x01
#define NGX_QUIC_ERR_CONNECTION_REFUSED 0x02
#define NGX_QUIC_ERR_FLOW_CONTROL_ERROR 0x03
#define NGX_QUIC_ERR_STREAM_LIMIT_ERROR 0x04
#define NGX_QUIC_ERR_STREAM_STATE_ERROR 0x05
#define NGX_QUIC_ERR_FINAL_SIZE_ERROR 0x06
#define NGX_QUIC_ERR_FRAME_ENCODING_ERROR 0x07
#define NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR 0x08
#define NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR 0x09
#define NGX_QUIC_ERR_PROTOCOL_VIOLATION 0x0A
#define NGX_QUIC_ERR_INVALID_TOKEN 0x0B
#define NGX_QUIC_ERR_APPLICATION_ERROR 0x0C
#define NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED 0x0D
#define NGX_QUIC_ERR_KEY_UPDATE_ERROR 0x0E
#define NGX_QUIC_ERR_AEAD_LIMIT_REACHED 0x0F
#define NGX_QUIC_ERR_NO_VIABLE_PATH 0x10
#define NGX_QUIC_ERR_CRYPTO_ERROR 0x100
#define NGX_QUIC_ERR_CRYPTO(e) (NGX_QUIC_ERR_CRYPTO_ERROR + (e))
/* 22.3. QUIC Transport Parameters Registry */
#define NGX_QUIC_TP_ORIGINAL_DCID 0x00
#define NGX_QUIC_TP_MAX_IDLE_TIMEOUT 0x01
#define NGX_QUIC_TP_SR_TOKEN 0x02
#define NGX_QUIC_TP_MAX_UDP_PAYLOAD_SIZE 0x03
#define NGX_QUIC_TP_INITIAL_MAX_DATA 0x04
#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL 0x05
#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE 0x06
#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI 0x07
#define NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI 0x08
#define NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI 0x09
#define NGX_QUIC_TP_ACK_DELAY_EXPONENT 0x0A
#define NGX_QUIC_TP_MAX_ACK_DELAY 0x0B
#define NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION 0x0C
#define NGX_QUIC_TP_PREFERRED_ADDRESS 0x0D
#define NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT 0x0E
#define NGX_QUIC_TP_INITIAL_SCID 0x0F
#define NGX_QUIC_TP_RETRY_SCID 0x10
#define NGX_QUIC_CID_LEN_MIN 8
#define NGX_QUIC_CID_LEN_MAX 20
#define NGX_QUIC_MAX_RANGES 10
typedef struct {
uint64_t gap;
uint64_t range;
} ngx_quic_ack_range_t;
typedef struct {
uint64_t largest;
uint64_t delay;
uint64_t range_count;
uint64_t first_range;
uint64_t ect0;
uint64_t ect1;
uint64_t ce;
uint64_t ranges_length;
} ngx_quic_ack_frame_t;
typedef struct {
uint64_t seqnum;
uint64_t retire;
uint8_t len;
u_char cid[NGX_QUIC_CID_LEN_MAX];
u_char srt[NGX_QUIC_SR_TOKEN_LEN];
} ngx_quic_new_conn_id_frame_t;
typedef struct {
uint64_t length;
} ngx_quic_new_token_frame_t;
/*
* common layout for CRYPTO and STREAM frames;
* conceptually, CRYPTO frame is also a stream
* frame lacking some properties
*/
typedef struct {
uint64_t offset;
uint64_t length;
} ngx_quic_ordered_frame_t;
typedef ngx_quic_ordered_frame_t ngx_quic_crypto_frame_t;
typedef struct {
/* initial fields same as in ngx_quic_ordered_frame_t */
uint64_t offset;
uint64_t length;
uint64_t stream_id;
unsigned off:1;
unsigned len:1;
unsigned fin:1;
} ngx_quic_stream_frame_t;
typedef struct {
uint64_t max_data;
} ngx_quic_max_data_frame_t;
typedef struct {
uint64_t error_code;
uint64_t frame_type;
ngx_str_t reason;
} ngx_quic_close_frame_t;
typedef struct {
uint64_t id;
uint64_t error_code;
uint64_t final_size;
} ngx_quic_reset_stream_frame_t;
typedef struct {
uint64_t id;
uint64_t error_code;
} ngx_quic_stop_sending_frame_t;
typedef struct {
uint64_t limit;
ngx_uint_t bidi; /* unsigned: bidi:1 */
} ngx_quic_streams_blocked_frame_t;
typedef struct {
uint64_t limit;
ngx_uint_t bidi; /* unsigned: bidi:1 */
} ngx_quic_max_streams_frame_t;
typedef struct {
uint64_t id;
uint64_t limit;
} ngx_quic_max_stream_data_frame_t;
typedef struct {
uint64_t limit;
} ngx_quic_data_blocked_frame_t;
typedef struct {
uint64_t id;
uint64_t limit;
} ngx_quic_stream_data_blocked_frame_t;
typedef struct {
uint64_t sequence_number;
} ngx_quic_retire_cid_frame_t;
typedef struct {
u_char data[8];
} ngx_quic_path_challenge_frame_t;
typedef struct ngx_quic_frame_s ngx_quic_frame_t;
struct ngx_quic_frame_s {
ngx_uint_t type;
enum ssl_encryption_level_t level;
ngx_queue_t queue;
uint64_t pnum;
size_t plen;
ngx_msec_t send_time;
ssize_t len;
unsigned need_ack:1;
unsigned pkt_need_ack:1;
unsigned ignore_congestion:1;
ngx_chain_t *data;
union {
ngx_quic_ack_frame_t ack;
ngx_quic_crypto_frame_t crypto;
ngx_quic_ordered_frame_t ord;
ngx_quic_new_conn_id_frame_t ncid;
ngx_quic_new_token_frame_t token;
ngx_quic_stream_frame_t stream;
ngx_quic_max_data_frame_t max_data;
ngx_quic_close_frame_t close;
ngx_quic_reset_stream_frame_t reset_stream;
ngx_quic_stop_sending_frame_t stop_sending;
ngx_quic_streams_blocked_frame_t streams_blocked;
ngx_quic_max_streams_frame_t max_streams;
ngx_quic_max_stream_data_frame_t max_stream_data;
ngx_quic_data_blocked_frame_t data_blocked;
ngx_quic_stream_data_blocked_frame_t stream_data_blocked;
ngx_quic_retire_cid_frame_t retire_cid;
ngx_quic_path_challenge_frame_t path_challenge;
ngx_quic_path_challenge_frame_t path_response;
} u;
};
typedef struct {
ngx_log_t *log;
ngx_quic_path_t *path;
ngx_quic_keys_t *keys;
ngx_msec_t received;
uint64_t number;
uint8_t num_len;
uint32_t trunc;
uint8_t flags;
uint32_t version;
ngx_str_t token;
enum ssl_encryption_level_t level;
ngx_uint_t error;
/* filled in by parser */
ngx_buf_t *raw; /* udp datagram */
u_char *data; /* quic packet */
size_t len;
/* cleartext fields */
ngx_str_t odcid; /* retry packet tag */
u_char odcid_buf[NGX_QUIC_MAX_CID_LEN];
ngx_str_t dcid;
ngx_str_t scid;
uint64_t pn;
u_char *plaintext;
ngx_str_t payload; /* decrypted data */
unsigned need_ack:1;
unsigned key_phase:1;
unsigned key_update:1;
unsigned parsed:1;
unsigned decrypted:1;
unsigned validated:1;
unsigned retried:1;
unsigned first:1;
unsigned rebound:1;
unsigned path_challenged:1;
} ngx_quic_header_t;
typedef struct {
ngx_msec_t max_idle_timeout;
ngx_msec_t max_ack_delay;
size_t max_udp_payload_size;
size_t initial_max_data;
size_t initial_max_stream_data_bidi_local;
size_t initial_max_stream_data_bidi_remote;
size_t initial_max_stream_data_uni;
ngx_uint_t initial_max_streams_bidi;
ngx_uint_t initial_max_streams_uni;
ngx_uint_t ack_delay_exponent;
ngx_uint_t active_connection_id_limit;
ngx_flag_t disable_active_migration;
ngx_str_t original_dcid;
ngx_str_t initial_scid;
ngx_str_t retry_scid;
u_char sr_token[NGX_QUIC_SR_TOKEN_LEN];
/* TODO */
void *preferred_address;
} ngx_quic_tp_t;
ngx_int_t ngx_quic_parse_packet(ngx_quic_header_t *pkt);
size_t ngx_quic_create_version_negotiation(ngx_quic_header_t *pkt, u_char *out);
size_t ngx_quic_payload_size(ngx_quic_header_t *pkt, size_t pkt_len);
size_t ngx_quic_create_header(ngx_quic_header_t *pkt, u_char *out,
u_char **pnp);
size_t ngx_quic_create_retry_itag(ngx_quic_header_t *pkt, u_char *out,
u_char **start);
ssize_t ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end,
ngx_quic_frame_t *frame);
ssize_t ngx_quic_create_frame(u_char *p, ngx_quic_frame_t *f);
ssize_t ngx_quic_parse_ack_range(ngx_log_t *log, u_char *start,
u_char *end, uint64_t *gap, uint64_t *range);
size_t ngx_quic_create_ack_range(u_char *p, uint64_t gap, uint64_t range);
ngx_int_t ngx_quic_init_transport_params(ngx_quic_tp_t *tp,
ngx_quic_conf_t *qcf);
ngx_int_t ngx_quic_parse_transport_params(u_char *p, u_char *end,
ngx_quic_tp_t *tp, ngx_log_t *log);
ssize_t ngx_quic_create_transport_params(u_char *p, u_char *end,
ngx_quic_tp_t *tp, size_t *clen);
void ngx_quic_dcid_encode_key(u_char *dcid, uint64_t key);
#endif /* _NGX_EVENT_QUIC_TRANSPORT_H_INCLUDED_ */

View file

@ -0,0 +1,420 @@
/*
* Copyright (C) Roman Arutyunyan
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_quic_connection.h>
static void ngx_quic_close_accepted_connection(ngx_connection_t *c);
static ngx_connection_t *ngx_quic_lookup_connection(ngx_listening_t *ls,
ngx_str_t *key, struct sockaddr *local_sockaddr, socklen_t local_socklen);
void
ngx_quic_recvmsg(ngx_event_t *ev)
{
ssize_t n;
ngx_str_t key;
ngx_buf_t buf;
ngx_log_t *log;
ngx_err_t err;
socklen_t socklen, local_socklen;
ngx_event_t *rev, *wev;
struct iovec iov[1];
struct msghdr msg;
ngx_sockaddr_t sa, lsa;
struct sockaddr *sockaddr, *local_sockaddr;
ngx_listening_t *ls;
ngx_event_conf_t *ecf;
ngx_connection_t *c, *lc;
ngx_quic_socket_t *qsock;
static u_char buffer[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
#if (NGX_HAVE_ADDRINFO_CMSG)
u_char msg_control[CMSG_SPACE(sizeof(ngx_addrinfo_t))];
#endif
if (ev->timedout) {
if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
return;
}
ev->timedout = 0;
}
ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
ev->available = ecf->multi_accept;
}
lc = ev->data;
ls = lc->listening;
ev->ready = 0;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"quic recvmsg on %V, ready: %d",
&ls->addr_text, ev->available);
do {
ngx_memzero(&msg, sizeof(struct msghdr));
iov[0].iov_base = (void *) buffer;
iov[0].iov_len = sizeof(buffer);
msg.msg_name = &sa;
msg.msg_namelen = sizeof(ngx_sockaddr_t);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
#if (NGX_HAVE_ADDRINFO_CMSG)
if (ls->wildcard) {
msg.msg_control = &msg_control;
msg.msg_controllen = sizeof(msg_control);
ngx_memzero(&msg_control, sizeof(msg_control));
}
#endif
n = recvmsg(lc->fd, &msg, 0);
if (n == -1) {
err = ngx_socket_errno;
if (err == NGX_EAGAIN) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
"quic recvmsg() not ready");
return;
}
ngx_log_error(NGX_LOG_ALERT, ev->log, err, "quic recvmsg() failed");
return;
}
#if (NGX_HAVE_ADDRINFO_CMSG)
if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"quic recvmsg() truncated data");
continue;
}
#endif
sockaddr = msg.msg_name;
socklen = msg.msg_namelen;
if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
socklen = sizeof(ngx_sockaddr_t);
}
#if (NGX_HAVE_UNIX_DOMAIN)
if (sockaddr->sa_family == AF_UNIX) {
struct sockaddr_un *saun = (struct sockaddr_un *) sockaddr;
if (socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)
|| saun->sun_path[0] == '\0')
{
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
"unbound unix socket");
goto next;
}
}
#endif
local_sockaddr = ls->sockaddr;
local_socklen = ls->socklen;
#if (NGX_HAVE_ADDRINFO_CMSG)
if (ls->wildcard) {
struct cmsghdr *cmsg;
ngx_memcpy(&lsa, local_sockaddr, local_socklen);
local_sockaddr = &lsa.sockaddr;
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (ngx_get_srcaddr_cmsg(cmsg, local_sockaddr) == NGX_OK) {
break;
}
}
}
#endif
if (ngx_quic_get_packet_dcid(ev->log, buffer, n, &key) != NGX_OK) {
goto next;
}
c = ngx_quic_lookup_connection(ls, &key, local_sockaddr, local_socklen);
if (c) {
#if (NGX_DEBUG)
if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
ngx_log_handler_pt handler;
handler = c->log->handler;
c->log->handler = NULL;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic recvmsg: fd:%d n:%z", c->fd, n);
c->log->handler = handler;
}
#endif
ngx_memzero(&buf, sizeof(ngx_buf_t));
buf.pos = buffer;
buf.last = buffer + n;
buf.start = buf.pos;
buf.end = buffer + sizeof(buffer);
qsock = ngx_quic_get_socket(c);
ngx_memcpy(&qsock->sockaddr, sockaddr, socklen);
qsock->socklen = socklen;
c->udp->buffer = &buf;
rev = c->read;
rev->ready = 1;
rev->active = 0;
rev->handler(rev);
if (c->udp) {
c->udp->buffer = NULL;
}
rev->ready = 0;
rev->active = 1;
goto next;
}
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
#endif
ngx_accept_disabled = ngx_cycle->connection_n / 8
- ngx_cycle->free_connection_n;
c = ngx_get_connection(lc->fd, ev->log);
if (c == NULL) {
return;
}
c->shared = 1;
c->type = SOCK_DGRAM;
c->socklen = socklen;
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, 1);
#endif
c->pool = ngx_create_pool(ls->pool_size, ev->log);
if (c->pool == NULL) {
ngx_quic_close_accepted_connection(c);
return;
}
c->sockaddr = ngx_palloc(c->pool, NGX_SOCKADDRLEN);
if (c->sockaddr == NULL) {
ngx_quic_close_accepted_connection(c);
return;
}
ngx_memcpy(c->sockaddr, sockaddr, socklen);
log = ngx_palloc(c->pool, sizeof(ngx_log_t));
if (log == NULL) {
ngx_quic_close_accepted_connection(c);
return;
}
*log = ls->log;
c->log = log;
c->pool->log = log;
c->listening = ls;
if (local_sockaddr == &lsa.sockaddr) {
local_sockaddr = ngx_palloc(c->pool, local_socklen);
if (local_sockaddr == NULL) {
ngx_quic_close_accepted_connection(c);
return;
}
ngx_memcpy(local_sockaddr, &lsa, local_socklen);
}
c->local_sockaddr = local_sockaddr;
c->local_socklen = local_socklen;
c->buffer = ngx_create_temp_buf(c->pool, n);
if (c->buffer == NULL) {
ngx_quic_close_accepted_connection(c);
return;
}
c->buffer->last = ngx_cpymem(c->buffer->last, buffer, n);
rev = c->read;
wev = c->write;
rev->active = 1;
wev->ready = 1;
rev->log = log;
wev->log = log;
/*
* TODO: MT: - ngx_atomic_fetch_add()
* or protection by critical section or light mutex
*
* TODO: MP: - allocated in a shared memory
* - ngx_atomic_fetch_add()
* or protection by critical section or light mutex
*/
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
c->start_time = ngx_current_msec;
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
#endif
if (ls->addr_ntop) {
c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
if (c->addr_text.data == NULL) {
ngx_quic_close_accepted_connection(c);
return;
}
c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
c->addr_text.data,
ls->addr_text_max_len, 0);
if (c->addr_text.len == 0) {
ngx_quic_close_accepted_connection(c);
return;
}
}
#if (NGX_DEBUG)
{
ngx_str_t addr;
u_char text[NGX_SOCKADDR_STRLEN];
ngx_debug_accepted_connection(ecf, c);
if (log->log_level & NGX_LOG_DEBUG_EVENT) {
addr.data = text;
addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,
NGX_SOCKADDR_STRLEN, 1);
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,
"*%uA quic recvmsg: %V fd:%d n:%z",
c->number, &addr, c->fd, n);
}
}
#endif
log->data = NULL;
log->handler = NULL;
ls->handler(c);
next:
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
ev->available -= n;
}
} while (ev->available);
}
static void
ngx_quic_close_accepted_connection(ngx_connection_t *c)
{
ngx_free_connection(c);
c->fd = (ngx_socket_t) -1;
if (c->pool) {
ngx_destroy_pool(c->pool);
}
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, -1);
#endif
}
static ngx_connection_t *
ngx_quic_lookup_connection(ngx_listening_t *ls, ngx_str_t *key,
struct sockaddr *local_sockaddr, socklen_t local_socklen)
{
uint32_t hash;
ngx_int_t rc;
ngx_connection_t *c;
ngx_rbtree_node_t *node, *sentinel;
ngx_quic_socket_t *qsock;
if (key->len == 0) {
return NULL;
}
node = ls->rbtree.root;
sentinel = ls->rbtree.sentinel;
hash = ngx_crc32_long(key->data, key->len);
while (node != sentinel) {
if (hash < node->key) {
node = node->left;
continue;
}
if (hash > node->key) {
node = node->right;
continue;
}
/* hash == node->key */
qsock = (ngx_quic_socket_t *) node;
rc = ngx_memn2cmp(key->data, qsock->sid.id, key->len, qsock->sid.len);
c = qsock->udp.connection;
if (rc == 0 && ls->wildcard) {
rc = ngx_cmp_sockaddr(local_sockaddr, local_socklen,
c->local_sockaddr, c->local_socklen, 1);
}
if (rc == 0) {
c->udp = &qsock->udp;
return c;
}
node = (rc < 0) ? node->left : node->right;
}
return NULL;
}

View file

@ -148,7 +148,7 @@ ngx_http_access_handler(ngx_http_request_t *r)
p = sin6->sin6_addr.s6_addr;
if (alcf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
addr = p[12] << 24;
addr = (in_addr_t) p[12] << 24;
addr += p[13] << 16;
addr += p[14] << 8;
addr += p[15];

View file

@ -2048,7 +2048,10 @@ ngx_http_fastcgi_process_header(ngx_http_request_t *r)
}
u->headers_in.status_n = status;
u->headers_in.status_line = *status_line;
if (status_line->len > 3) {
u->headers_in.status_line = *status_line;
}
} else if (u->headers_in.location) {
u->headers_in.status_n = 302;

View file

@ -199,7 +199,7 @@ ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
p = inaddr6->s6_addr;
if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
inaddr = p[12] << 24;
inaddr = (in_addr_t) p[12] << 24;
inaddr += p[13] << 16;
inaddr += p[14] << 8;
inaddr += p[15];
@ -272,7 +272,7 @@ ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
p = inaddr6->s6_addr;
inaddr = p[12] << 24;
inaddr = (in_addr_t) p[12] << 24;
inaddr += p[13] << 16;
inaddr += p[14] << 8;
inaddr += p[15];
@ -1259,7 +1259,7 @@ ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
return gvvn->value;
}
val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
val = ngx_pcalloc(ctx->pool, sizeof(ngx_http_variable_value_t));
if (val == NULL) {
return NULL;
}
@ -1271,8 +1271,6 @@ ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
}
val->valid = 1;
val->no_cacheable = 0;
val->not_found = 0;
gvvn = ngx_palloc(ctx->temp_pool,
sizeof(ngx_http_geo_variable_value_node_t));

View file

@ -266,7 +266,7 @@ ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
p = inaddr6->s6_addr;
inaddr = p[12] << 24;
inaddr = (in_addr_t) p[12] << 24;
inaddr += p[13] << 16;
inaddr += p[14] << 8;
inaddr += p[15];

View file

@ -631,7 +631,7 @@ ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the using of the regex \"%V\" requires PCRE library",
"using regex \"%V\" requires PCRE library",
name);
return NGX_ERROR;

View file

@ -489,6 +489,7 @@ ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
if (cf->args->nelts == 2) {
ngx_str_set(&ret->text.value, "");
return NGX_CONF_OK;
}

View file

@ -1153,7 +1153,10 @@ ngx_http_scgi_process_header(ngx_http_request_t *r)
}
u->headers_in.status_n = status;
u->headers_in.status_line = *status_line;
if (status_line->len > 3) {
u->headers_in.status_line = *status_line;
}
} else if (u->headers_in.location) {
u->headers_in.status_n = 302;

View file

@ -2001,7 +2001,7 @@ ngx_http_ssi_regex_match(ngx_http_request_t *r, ngx_str_t *pattern,
#else
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"the using of the regex \"%V\" in SSI requires PCRE library",
"using regex \"%V\" in SSI requires PCRE library",
pattern);
return NGX_HTTP_SSI_ERROR;

View file

@ -9,6 +9,10 @@
#include <ngx_core.h>
#include <ngx_http.h>
#if (NGX_QUIC_OPENSSL_COMPAT)
#include <ngx_event_quic_openssl_compat.h>
#endif
typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
ngx_pool_t *pool, ngx_str_t *s);
@ -39,8 +43,6 @@ static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
static ngx_int_t ngx_http_ssl_compile_certificates(ngx_conf_t *cf,
ngx_http_ssl_srv_conf_t *conf);
static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
@ -52,6 +54,10 @@ static char *ngx_http_ssl_conf_command_check(ngx_conf_t *cf, void *post,
void *data);
static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
#if (NGX_QUIC_OPENSSL_COMPAT)
static ngx_int_t ngx_http_ssl_quic_compat_init(ngx_conf_t *cf,
ngx_http_conf_addr_t *addr);
#endif
static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = {
@ -82,24 +88,12 @@ static ngx_conf_enum_t ngx_http_ssl_ocsp[] = {
};
static ngx_conf_deprecated_t ngx_http_ssl_deprecated = {
ngx_conf_deprecated, "ssl", "listen ... ssl"
};
static ngx_conf_post_t ngx_http_ssl_conf_command_post =
{ ngx_http_ssl_conf_command_check };
static ngx_command_t ngx_http_ssl_commands[] = {
{ ngx_string("ssl"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_http_ssl_enable,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, enable),
&ngx_http_ssl_deprecated },
{ ngx_string("ssl_certificate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
@ -419,16 +413,22 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,
unsigned char *outlen, const unsigned char *in, unsigned int inlen,
void *arg)
{
unsigned int srvlen;
unsigned char *srv;
unsigned int srvlen;
unsigned char *srv;
#if (NGX_DEBUG)
unsigned int i;
unsigned int i;
#endif
#if (NGX_HTTP_V2 || NGX_HTTP_V3)
ngx_http_connection_t *hc;
#endif
#if (NGX_HTTP_V2)
ngx_http_connection_t *hc;
ngx_http_v2_srv_conf_t *h2scf;
#endif
#if (NGX_HTTP_V2 || NGX_DEBUG)
ngx_connection_t *c;
#if (NGX_HTTP_V3)
ngx_http_v3_srv_conf_t *h3scf;
#endif
#if (NGX_HTTP_V2 || NGX_HTTP_V3 || NGX_DEBUG)
ngx_connection_t *c;
c = ngx_ssl_get_connection(ssl_conn);
#endif
@ -441,17 +441,49 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,
}
#endif
#if (NGX_HTTP_V2)
#if (NGX_HTTP_V2 || NGX_HTTP_V3)
hc = c->data;
#endif
#if (NGX_HTTP_V3)
if (hc->addr_conf->quic) {
h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);
if (h3scf->enable && h3scf->enable_hq) {
srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO
NGX_HTTP_V3_HQ_ALPN_PROTO;
srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO NGX_HTTP_V3_HQ_ALPN_PROTO)
- 1;
} else if (h3scf->enable_hq) {
srv = (unsigned char *) NGX_HTTP_V3_HQ_ALPN_PROTO;
srvlen = sizeof(NGX_HTTP_V3_HQ_ALPN_PROTO) - 1;
} else if (h3scf->enable) {
srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO;
srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1;
} else {
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
if (hc->addr_conf->http2) {
srv = (unsigned char *) NGX_HTTP_V2_ALPN_PROTO NGX_HTTP_ALPN_PROTOS;
srvlen = sizeof(NGX_HTTP_V2_ALPN_PROTO NGX_HTTP_ALPN_PROTOS) - 1;
} else
#endif
{
srv = (unsigned char *) NGX_HTTP_ALPN_PROTOS;
srvlen = sizeof(NGX_HTTP_ALPN_PROTOS) - 1;
#if (NGX_HTTP_V2)
h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);
if (h2scf->enable || hc->addr_conf->http2) {
srv = (unsigned char *) NGX_HTTP_V2_ALPN_PROTO NGX_HTTP_ALPN_PROTOS;
srvlen = sizeof(NGX_HTTP_V2_ALPN_PROTO NGX_HTTP_ALPN_PROTOS) - 1;
} else
#endif
{
srv = (unsigned char *) NGX_HTTP_ALPN_PROTOS;
srvlen = sizeof(NGX_HTTP_ALPN_PROTOS) - 1;
}
}
if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen,
@ -579,7 +611,6 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
* sscf->stapling_responder = { 0, NULL };
*/
sscf->enable = NGX_CONF_UNSET;
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
sscf->early_data = NGX_CONF_UNSET;
sscf->reject_handshake = NGX_CONF_UNSET;
@ -611,17 +642,6 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_pool_cleanup_t *cln;
if (conf->enable == NGX_CONF_UNSET) {
if (prev->enable == NGX_CONF_UNSET) {
conf->enable = 0;
} else {
conf->enable = prev->enable;
conf->file = prev->file;
conf->line = prev->line;
}
}
ngx_conf_merge_value(conf->session_timeout,
prev->session_timeout, 300);
@ -676,37 +696,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
conf->ssl.log = cf->log;
if (conf->enable) {
if (conf->certificates) {
if (conf->certificate_keys == NULL) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate_key\" is defined for "
"the \"ssl\" directive in %s:%ui",
conf->file, conf->line);
return NGX_CONF_ERROR;
}
if (conf->certificate_keys->nelts < conf->certificates->nelts) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate_key\" is defined "
"for certificate \"%V\" and "
"the \"ssl\" directive in %s:%ui",
((ngx_str_t *) conf->certificates->elts)
+ conf->certificates->nelts - 1,
conf->file, conf->line);
return NGX_CONF_ERROR;
}
} else if (!conf->reject_handshake) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate\" is defined for "
"the \"ssl\" directive in %s:%ui",
conf->file, conf->line);
return NGX_CONF_ERROR;
}
} else if (conf->certificates) {
if (conf->certificates) {
if (conf->certificate_keys == NULL
|| conf->certificate_keys->nelts < conf->certificates->nelts)
@ -992,26 +982,6 @@ found:
}
static char *
ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_ssl_srv_conf_t *sscf = conf;
char *rv;
rv = ngx_conf_set_flag_slot(cf, cmd, conf);
if (rv != NGX_CONF_OK) {
return rv;
}
sscf->file = cf->conf_file->file.name.data;
sscf->line = cf->conf_file->line;
return NGX_CONF_OK;
}
static char *
ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
@ -1241,6 +1211,7 @@ static ngx_int_t
ngx_http_ssl_init(ngx_conf_t *cf)
{
ngx_uint_t a, p, s;
const char *name;
ngx_http_conf_addr_t *addr;
ngx_http_conf_port_t *port;
ngx_http_ssl_srv_conf_t *sscf;
@ -1290,22 +1261,44 @@ ngx_http_ssl_init(ngx_conf_t *cf)
addr = port[p].addrs.elts;
for (a = 0; a < port[p].addrs.nelts; a++) {
if (!addr[a].opt.ssl) {
if (!addr[a].opt.ssl && !addr[a].opt.quic) {
continue;
}
if (addr[a].opt.quic) {
name = "quic";
#if (NGX_QUIC_OPENSSL_COMPAT)
if (ngx_http_ssl_quic_compat_init(cf, &addr[a]) != NGX_OK) {
return NGX_ERROR;
}
#endif
} else {
name = "ssl";
}
cscf = addr[a].default_server;
sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
if (sscf->certificates) {
if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"\"ssl_protocols\" must enable TLSv1.3 for "
"the \"listen ... %s\" directive in %s:%ui",
name, cscf->file_name, cscf->line);
return NGX_ERROR;
}
continue;
}
if (!sscf->reject_handshake) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate\" is defined for "
"the \"listen ... ssl\" directive in %s:%ui",
cscf->file_name, cscf->line);
"the \"listen ... %s\" directive in %s:%ui",
name, cscf->file_name, cscf->line);
return NGX_ERROR;
}
@ -1326,8 +1319,8 @@ ngx_http_ssl_init(ngx_conf_t *cf)
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate\" is defined for "
"the \"listen ... ssl\" directive in %s:%ui",
cscf->file_name, cscf->line);
"the \"listen ... %s\" directive in %s:%ui",
name, cscf->file_name, cscf->line);
return NGX_ERROR;
}
}
@ -1335,3 +1328,31 @@ ngx_http_ssl_init(ngx_conf_t *cf)
return NGX_OK;
}
#if (NGX_QUIC_OPENSSL_COMPAT)
static ngx_int_t
ngx_http_ssl_quic_compat_init(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
{
ngx_uint_t s;
ngx_http_ssl_srv_conf_t *sscf;
ngx_http_core_srv_conf_t **cscfp, *cscf;
cscfp = addr->servers.elts;
for (s = 0; s < addr->servers.nelts; s++) {
cscf = cscfp[s];
sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
if (sscf->certificates || sscf->reject_handshake) {
if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) {
return NGX_ERROR;
}
}
}
return NGX_OK;
}
#endif

View file

@ -15,8 +15,6 @@
typedef struct {
ngx_flag_t enable;
ngx_ssl_t ssl;
ngx_flag_t prefer_server_ciphers;
@ -64,9 +62,6 @@ typedef struct {
ngx_flag_t stapling_verify;
ngx_str_t stapling_file;
ngx_str_t stapling_responder;
u_char *file;
ngx_uint_t line;
} ngx_http_ssl_srv_conf_t;

View file

@ -1381,7 +1381,10 @@ ngx_http_uwsgi_process_header(ngx_http_request_t *r)
}
u->headers_in.status_n = status;
u->headers_in.status_line = *status_line;
if (status_line->len > 3) {
u->headers_in.status_line = *status_line;
}
} else if (u->headers_in.location) {
u->headers_in.status_n = 302;

View file

@ -1200,7 +1200,10 @@ ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
port = cmcf->ports->elts;
for (i = 0; i < cmcf->ports->nelts; i++) {
if (p != port[i].port || sa->sa_family != port[i].family) {
if (p != port[i].port
|| lsopt->type != port[i].type
|| sa->sa_family != port[i].family)
{
continue;
}
@ -1217,6 +1220,7 @@ ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
}
port->family = sa->sa_family;
port->type = lsopt->type;
port->port = p;
port->addrs.elts = NULL;
@ -1237,6 +1241,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_V2)
ngx_uint_t http2;
#endif
#if (NGX_HTTP_V3)
ngx_uint_t quic;
#endif
/*
* we cannot compare whole sockaddr struct's as kernel
@ -1278,6 +1285,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
protocols |= lsopt->http2 << 2;
protocols_prev |= addr[i].opt.http2 << 2;
#endif
#if (NGX_HTTP_V3)
quic = lsopt->quic || addr[i].opt.quic;
#endif
if (lsopt->set) {
@ -1365,6 +1375,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_V2)
addr[i].opt.http2 = http2;
#endif
#if (NGX_HTTP_V3)
addr[i].opt.quic = quic;
#endif
return NGX_OK;
}
@ -1831,6 +1844,7 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
}
#endif
ls->type = addr->opt.type;
ls->backlog = addr->opt.backlog;
ls->rcvbuf = addr->opt.rcvbuf;
ls->sndbuf = addr->opt.sndbuf;
@ -1866,6 +1880,12 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
ls->reuseport = addr->opt.reuseport;
#endif
ls->wildcard = addr->opt.wildcard;
#if (NGX_HTTP_V3)
ls->quic = addr->opt.quic;
#endif
return ls;
}
@ -1897,6 +1917,9 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
#endif
#if (NGX_HTTP_V2)
addrs[i].conf.http2 = addr[i].opt.http2;
#endif
#if (NGX_HTTP_V3)
addrs[i].conf.quic = addr[i].opt.quic;
#endif
addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
@ -1962,6 +1985,9 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
#endif
#if (NGX_HTTP_V2)
addrs6[i].conf.http2 = addr[i].opt.http2;
#endif
#if (NGX_HTTP_V3)
addrs6[i].conf.quic = addr[i].opt.quic;
#endif
addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;

View file

@ -20,6 +20,8 @@ typedef struct ngx_http_file_cache_s ngx_http_file_cache_t;
typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t;
typedef struct ngx_http_chunked_s ngx_http_chunked_t;
typedef struct ngx_http_v2_stream_s ngx_http_v2_stream_t;
typedef struct ngx_http_v3_parse_s ngx_http_v3_parse_t;
typedef struct ngx_http_v3_session_s ngx_http_v3_session_t;
typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
@ -38,6 +40,9 @@ typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r,
#if (NGX_HTTP_V2)
#include <ngx_http_v2.h>
#endif
#if (NGX_HTTP_V3)
#include <ngx_http_v3.h>
#endif
#if (NGX_HTTP_CACHE)
#include <ngx_http_cache.h>
#endif
@ -124,6 +129,11 @@ void ngx_http_handler(ngx_http_request_t *r);
void ngx_http_run_posted_requests(ngx_connection_t *c);
ngx_int_t ngx_http_post_request(ngx_http_request_t *r,
ngx_http_posted_request_t *pr);
ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,
ngx_str_t *host);
ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,
ngx_uint_t alloc);
void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc);
void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc);
@ -167,7 +177,7 @@ ngx_uint_t ngx_http_degraded(ngx_http_request_t *);
#endif
#if (NGX_HTTP_V2)
#if (NGX_HTTP_V2 || NGX_HTTP_V3)
ngx_int_t ngx_http_huff_decode(u_char *state, u_char *src, size_t len,
u_char **dst, ngx_uint_t last, ngx_log_t *log);
size_t ngx_http_huff_encode(u_char *src, size_t len, u_char *dst,

View file

@ -170,6 +170,8 @@ ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)
file->aio->data = r;
file->aio->handler = ngx_http_copy_aio_event_handler;
ngx_add_timer(&file->aio->event, 60000);
r->main->blocked++;
r->aio = 1;
ctx->aio = 1;
@ -192,12 +194,32 @@ ngx_http_copy_aio_event_handler(ngx_event_t *ev)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http aio: \"%V?%V\"", &r->uri, &r->args);
if (ev->timedout) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"aio operation took too long");
ev->timedout = 0;
return;
}
if (ev->timer_set) {
ngx_del_timer(ev);
}
r->main->blocked--;
r->aio = 0;
r->write_event_handler(r);
if (r->main->terminated) {
/*
* trigger connection event handler if the request was
* terminated
*/
ngx_http_run_posted_requests(c);
c->write->handler(c->write);
} else {
r->write_event_handler(r);
ngx_http_run_posted_requests(c);
}
}
#endif
@ -264,6 +286,8 @@ ngx_http_copy_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
return NGX_ERROR;
}
ngx_add_timer(&task->event, 60000);
r->main->blocked++;
r->aio = 1;
@ -288,6 +312,17 @@ ngx_http_copy_thread_event_handler(ngx_event_t *ev)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http thread: \"%V?%V\"", &r->uri, &r->args);
if (ev->timedout) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"thread operation took too long");
ev->timedout = 0;
return;
}
if (ev->timer_set) {
ngx_del_timer(ev);
}
r->main->blocked--;
r->aio = 0;
@ -305,11 +340,11 @@ ngx_http_copy_thread_event_handler(ngx_event_t *ev)
#endif
if (r->done) {
if (r->done || r->main->terminated) {
/*
* trigger connection event handler if the subrequest was
* already finalized; this can happen if the handler is used
* for sendfile() in threads
* already finalized (this can happen if the handler is used
* for sendfile() in threads), or if the request was terminated
*/
c->write->handler(c->write);

View file

@ -3005,6 +3005,7 @@ ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
lsopt.socklen = sizeof(struct sockaddr_in);
lsopt.backlog = NGX_LISTEN_BACKLOG;
lsopt.type = SOCK_STREAM;
lsopt.rcvbuf = -1;
lsopt.sndbuf = -1;
#if (NGX_HAVE_SETFIB)
@ -3960,7 +3961,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_str_t *value, size;
ngx_url_t u;
ngx_uint_t n, i;
ngx_uint_t n, i, backlog;
ngx_http_listen_opt_t lsopt;
cscf->listen = 1;
@ -3986,6 +3987,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
lsopt.backlog = NGX_LISTEN_BACKLOG;
lsopt.type = SOCK_STREAM;
lsopt.rcvbuf = -1;
lsopt.sndbuf = -1;
#if (NGX_HAVE_SETFIB)
@ -3998,6 +4000,8 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
lsopt.ipv6only = 1;
#endif
backlog = 0;
for (n = 2; n < cf->args->nelts; n++) {
if (ngx_strcmp(value[n].data, "default_server") == 0
@ -4056,6 +4060,8 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
backlog = 1;
continue;
}
@ -4174,6 +4180,11 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (ngx_strcmp(value[n].data, "http2") == 0) {
#if (NGX_HTTP_V2)
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"the \"listen ... http2\" directive "
"is deprecated, use "
"the \"http2\" directive instead");
lsopt.http2 = 1;
continue;
#else
@ -4184,6 +4195,19 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#endif
}
if (ngx_strcmp(value[n].data, "quic") == 0) {
#if (NGX_HTTP_V3)
lsopt.quic = 1;
lsopt.type = SOCK_DGRAM;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"quic\" parameter requires "
"ngx_http_v3_module");
return NGX_CONF_ERROR;
#endif
}
if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) {
if (ngx_strcmp(&value[n].data[13], "on") == 0) {
@ -4285,6 +4309,50 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
if (lsopt.quic) {
#if (NGX_HAVE_TCP_FASTOPEN)
if (lsopt.fastopen != -1) {
return "\"fastopen\" parameter is incompatible with \"quic\"";
}
#endif
if (backlog) {
return "\"backlog\" parameter is incompatible with \"quic\"";
}
#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
if (lsopt.accept_filter) {
return "\"accept_filter\" parameter is incompatible with \"quic\"";
}
#endif
#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
if (lsopt.deferred_accept) {
return "\"deferred\" parameter is incompatible with \"quic\"";
}
#endif
#if (NGX_HTTP_SSL)
if (lsopt.ssl) {
return "\"ssl\" parameter is incompatible with \"quic\"";
}
#endif
#if (NGX_HTTP_V2)
if (lsopt.http2) {
return "\"http2\" parameter is incompatible with \"quic\"";
}
#endif
if (lsopt.so_keepalive) {
return "\"so_keepalive\" parameter is incompatible with \"quic\"";
}
if (lsopt.proxy_protocol) {
return "\"proxy_protocol\" parameter is incompatible with \"quic\"";
}
}
for (n = 0; n < u.naddrs; n++) {
for (i = 0; i < n; i++) {

View file

@ -75,6 +75,7 @@ typedef struct {
unsigned wildcard:1;
unsigned ssl:1;
unsigned http2:1;
unsigned quic:1;
#if (NGX_HAVE_INET6)
unsigned ipv6only:1;
#endif
@ -86,6 +87,7 @@ typedef struct {
int backlog;
int rcvbuf;
int sndbuf;
int type;
#if (NGX_HAVE_SETFIB)
int setfib;
#endif
@ -237,6 +239,7 @@ struct ngx_http_addr_conf_s {
unsigned ssl:1;
unsigned http2:1;
unsigned quic:1;
unsigned proxy_protocol:1;
};
@ -266,6 +269,7 @@ typedef struct {
typedef struct {
ngx_int_t family;
ngx_int_t type;
in_port_t port;
ngx_array_t addrs; /* array of ngx_http_conf_addr_t */
} ngx_http_conf_port_t;

View file

@ -14,7 +14,7 @@
static ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r,
ngx_http_cache_t *c);
static void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev);
static void ngx_http_file_cache_lock_wait(ngx_http_request_t *r,
static ngx_int_t ngx_http_file_cache_lock_wait(ngx_http_request_t *r,
ngx_http_cache_t *c);
static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r,
ngx_http_cache_t *c);
@ -463,6 +463,7 @@ ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)
static void
ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)
{
ngx_int_t rc;
ngx_connection_t *c;
ngx_http_request_t *r;
@ -474,13 +475,31 @@ ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http file cache wait: \"%V?%V\"", &r->uri, &r->args);
ngx_http_file_cache_lock_wait(r, r->cache);
rc = ngx_http_file_cache_lock_wait(r, r->cache);
ngx_http_run_posted_requests(c);
if (rc == NGX_AGAIN) {
return;
}
r->cache->waiting = 0;
r->main->blocked--;
if (r->main->terminated) {
/*
* trigger connection event handler if the request was
* terminated
*/
c->write->handler(c->write);
} else {
r->write_event_handler(r);
ngx_http_run_posted_requests(c);
}
}
static void
static ngx_int_t
ngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)
{
ngx_uint_t wait;
@ -495,7 +514,7 @@ ngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"cache lock timeout");
c->lock_timeout = 0;
goto wakeup;
return NGX_OK;
}
cache = c->file_cache;
@ -513,14 +532,10 @@ ngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)
if (wait) {
ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
return;
return NGX_AGAIN;
}
wakeup:
c->waiting = 0;
r->main->blocked--;
r->write_event_handler(r);
return NGX_OK;
}
@ -690,6 +705,8 @@ ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
c->file.aio->data = r;
c->file.aio->handler = ngx_http_cache_aio_event_handler;
ngx_add_timer(&c->file.aio->event, 60000);
r->main->blocked++;
r->aio = 1;
@ -737,12 +754,32 @@ ngx_http_cache_aio_event_handler(ngx_event_t *ev)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http file cache aio: \"%V?%V\"", &r->uri, &r->args);
if (ev->timedout) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"aio operation took too long");
ev->timedout = 0;
return;
}
if (ev->timer_set) {
ngx_del_timer(ev);
}
r->main->blocked--;
r->aio = 0;
r->write_event_handler(r);
if (r->main->terminated) {
/*
* trigger connection event handler if the request was
* terminated
*/
ngx_http_run_posted_requests(c);
c->write->handler(c->write);
} else {
r->write_event_handler(r);
ngx_http_run_posted_requests(c);
}
}
#endif
@ -786,6 +823,8 @@ ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
return NGX_ERROR;
}
ngx_add_timer(&task->event, 60000);
r->main->blocked++;
r->aio = 1;
@ -807,12 +846,32 @@ ngx_http_cache_thread_event_handler(ngx_event_t *ev)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http file cache thread: \"%V?%V\"", &r->uri, &r->args);
if (ev->timedout) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"thread operation took too long");
ev->timedout = 0;
return;
}
if (ev->timer_set) {
ngx_del_timer(ev);
}
r->main->blocked--;
r->aio = 0;
r->write_event_handler(r);
if (r->main->terminated) {
/*
* trigger connection event handler if the request was
* terminated
*/
ngx_http_run_posted_requests(c);
c->write->handler(c->write);
} else {
r->write_event_handler(r);
ngx_http_run_posted_requests(c);
}
}
#endif

View file

@ -2657,7 +2657,7 @@ ngx_http_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
!= NGX_OK)
{
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"http2 huffman decoding error at state %d: "
"http huffman decoding error at state %d: "
"bad code 0x%Xd", *state, ch >> 4);
return NGX_ERROR;
@ -2667,7 +2667,7 @@ ngx_http_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
!= NGX_OK)
{
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"http2 huffman decoding error at state %d: "
"http huffman decoding error at state %d: "
"bad code 0x%Xd", *state, ch & 0xf);
return NGX_ERROR;
@ -2677,7 +2677,7 @@ ngx_http_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
if (last) {
if (!ending) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
"http2 huffman decoding error: "
"http huffman decoding error: "
"incomplete code 0x%Xd", ch);
return NGX_ERROR;

View file

@ -451,19 +451,16 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
switch (ch) {
case '/':
r->port_end = p;
r->uri_start = p;
state = sw_after_slash_in_uri;
break;
case '?':
r->port_end = p;
r->uri_start = p;
r->args_start = p + 1;
r->empty_path_in_uri = 1;
state = sw_uri;
break;
case ' ':
r->port_end = p;
/*
* use single "/" from request line to preserve pointers,
* if request line will be copied to large client buffer

View file

@ -29,10 +29,6 @@ static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,
ngx_uint_t alloc);
static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,
ngx_str_t *host);
static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,
ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);
@ -50,7 +46,6 @@ static void ngx_http_keepalive_handler(ngx_event_t *ev);
static void ngx_http_set_lingering_close(ngx_connection_t *c);
static void ngx_http_lingering_close_handler(ngx_event_t *ev);
static ngx_int_t ngx_http_post_action(ngx_http_request_t *r);
static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error);
static void ngx_http_log_request(ngx_http_request_t *r);
static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len);
@ -323,24 +318,19 @@ ngx_http_init_connection(ngx_connection_t *c)
rev->handler = ngx_http_wait_request_handler;
c->write->handler = ngx_http_empty_handler;
#if (NGX_HTTP_V2)
if (hc->addr_conf->http2) {
rev->handler = ngx_http_v2_init;
#if (NGX_HTTP_V3)
if (hc->addr_conf->quic) {
ngx_http_v3_init_stream(c);
return;
}
#endif
#if (NGX_HTTP_SSL)
{
ngx_http_ssl_srv_conf_t *sscf;
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
if (sscf->enable || hc->addr_conf->ssl) {
if (hc->addr_conf->ssl) {
hc->ssl = 1;
c->log->action = "SSL handshaking";
rev->handler = ngx_http_ssl_handshake;
}
}
#endif
if (hc->addr_conf->proxy_protocol) {
@ -381,6 +371,9 @@ ngx_http_wait_request_handler(ngx_event_t *rev)
ngx_buf_t *b;
ngx_connection_t *c;
ngx_http_connection_t *hc;
#if (NGX_HTTP_V2)
ngx_http_v2_srv_conf_t *h2scf;
#endif
ngx_http_core_srv_conf_t *cscf;
c = rev->data;
@ -427,6 +420,8 @@ ngx_http_wait_request_handler(ngx_event_t *rev)
b->end = b->last + size;
}
size = b->end - b->last;
n = c->recv(c, b->last, size);
if (n == NGX_AGAIN) {
@ -441,12 +436,16 @@ ngx_http_wait_request_handler(ngx_event_t *rev)
return;
}
/*
* We are trying to not hold c->buffer's memory for an idle connection.
*/
if (b->pos == b->last) {
if (ngx_pfree(c->pool, b->start) == NGX_OK) {
b->start = NULL;
/*
* We are trying to not hold c->buffer's memory for an
* idle connection.
*/
if (ngx_pfree(c->pool, b->start) == NGX_OK) {
b->start = NULL;
}
}
return;
@ -487,6 +486,29 @@ ngx_http_wait_request_handler(ngx_event_t *rev)
}
}
#if (NGX_HTTP_V2)
h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);
if (!hc->ssl && (h2scf->enable || hc->addr_conf->http2)) {
size = ngx_min(sizeof(NGX_HTTP_V2_PREFACE) - 1,
(size_t) (b->last - b->pos));
if (ngx_memcmp(b->pos, NGX_HTTP_V2_PREFACE, size) == 0) {
if (size == sizeof(NGX_HTTP_V2_PREFACE) - 1) {
ngx_http_v2_init(rev);
return;
}
ngx_post_event(rev, &ngx_posted_events);
return;
}
}
#endif
c->log->action = "reading client request line";
ngx_reusable_connection(c, 0);
@ -806,13 +828,16 @@ ngx_http_ssl_handshake_handler(ngx_connection_t *c)
#if (NGX_HTTP_V2 \
&& defined TLSEXT_TYPE_application_layer_protocol_negotiation)
{
unsigned int len;
const unsigned char *data;
ngx_http_connection_t *hc;
unsigned int len;
const unsigned char *data;
ngx_http_connection_t *hc;
ngx_http_v2_srv_conf_t *h2scf;
hc = c->data;
if (hc->addr_conf->http2) {
h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);
if (h2scf->enable || hc->addr_conf->http2) {
SSL_get0_alpn_selected(c->ssl->connection, &data, &len);
@ -949,6 +974,14 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
#ifdef SSL_OP_NO_RENEGOTIATION
SSL_set_options(ssl_conn, SSL_OP_NO_RENEGOTIATION);
#endif
#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
#if (NGX_HTTP_V3)
if (c->listening->quic) {
SSL_clear_options(ssl_conn, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
}
#endif
#endif
}
@ -1685,14 +1718,23 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
r->request_end = new + (r->request_end - old);
}
r->method_end = new + (r->method_end - old);
if (r->method_end) {
r->method_end = new + (r->method_end - old);
}
r->uri_start = new + (r->uri_start - old);
r->uri_end = new + (r->uri_end - old);
if (r->uri_start) {
r->uri_start = new + (r->uri_start - old);
}
if (r->uri_end) {
r->uri_end = new + (r->uri_end - old);
}
if (r->schema_start) {
r->schema_start = new + (r->schema_start - old);
r->schema_end = new + (r->schema_end - old);
if (r->schema_end) {
r->schema_end = new + (r->schema_end - old);
}
}
if (r->host_start) {
@ -1702,11 +1744,6 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
}
}
if (r->port_start) {
r->port_start = new + (r->port_start - old);
r->port_end = new + (r->port_end - old);
}
if (r->uri_ext) {
r->uri_ext = new + (r->uri_ext - old);
}
@ -1721,9 +1758,18 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
} else {
r->header_name_start = new;
r->header_name_end = new + (r->header_name_end - old);
r->header_start = new + (r->header_start - old);
r->header_end = new + (r->header_end - old);
if (r->header_name_end) {
r->header_name_end = new + (r->header_name_end - old);
}
if (r->header_start) {
r->header_start = new + (r->header_start - old);
}
if (r->header_end) {
r->header_end = new + (r->header_end - old);
}
}
r->header_in = b;
@ -2095,7 +2141,7 @@ ngx_http_process_request(ngx_http_request_t *r)
}
static ngx_int_t
ngx_int_t
ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
{
u_char *h, ch;
@ -2187,7 +2233,7 @@ ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
}
static ngx_int_t
ngx_int_t
ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host)
{
ngx_int_t rc;
@ -2648,6 +2694,8 @@ ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc)
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http terminate request count:%d", mr->count);
mr->terminated = 1;
if (rc > 0 && (mr->headers_out.status == 0 || mr->connection->sent == 0)) {
mr->headers_out.status = rc;
}
@ -2670,8 +2718,11 @@ ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc)
if (mr->write_event_handler) {
if (mr->blocked) {
r = r->connection->data;
r->connection->error = 1;
r->write_event_handler = ngx_http_request_finalizer;
return;
}
@ -2710,6 +2761,13 @@ ngx_http_finalize_connection(ngx_http_request_t *r)
}
#endif
#if (NGX_HTTP_V3)
if (r->connection->quic) {
ngx_http_close_request(r, 0);
return;
}
#endif
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (r->main->count != 1) {
@ -2925,6 +2983,20 @@ ngx_http_test_reading(ngx_http_request_t *r)
#endif
#if (NGX_HTTP_V3)
if (c->quic) {
if (rev->error) {
c->error = 1;
err = 0;
goto closed;
}
return;
}
#endif
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
@ -3590,7 +3662,7 @@ ngx_http_post_action(ngx_http_request_t *r)
}
static void
void
ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_connection_t *c;
@ -3677,7 +3749,12 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)
log->action = "closing request";
if (r->connection->timedout) {
if (r->connection->timedout
#if (NGX_HTTP_V3)
&& r->connection->quic == NULL
#endif
)
{
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->reset_timedout_connection) {
@ -3750,6 +3827,12 @@ ngx_http_close_connection(ngx_connection_t *c)
#endif
#if (NGX_HTTP_V3)
if (c->quic) {
ngx_http_v3_reset_stream(c);
}
#endif
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, -1);
#endif

View file

@ -24,6 +24,7 @@
#define NGX_HTTP_VERSION_10 1000
#define NGX_HTTP_VERSION_11 1001
#define NGX_HTTP_VERSION_20 2000
#define NGX_HTTP_VERSION_30 3000
#define NGX_HTTP_UNKNOWN 0x00000001
#define NGX_HTTP_GET 0x00000002
@ -451,6 +452,7 @@ struct ngx_http_request_s {
ngx_http_connection_t *http_connection;
ngx_http_v2_stream_t *stream;
ngx_http_v3_parse_t *v3_parse;
ngx_http_log_handler_pt log_handler;
@ -543,10 +545,12 @@ struct ngx_http_request_s {
unsigned request_complete:1;
unsigned request_output:1;
unsigned header_sent:1;
unsigned response_sent:1;
unsigned expect_tested:1;
unsigned root_tested:1;
unsigned done:1;
unsigned logged:1;
unsigned terminated:1;
unsigned buffered:4;
@ -594,8 +598,6 @@ struct ngx_http_request_s {
u_char *schema_end;
u_char *host_start;
u_char *host_end;
u_char *port_start;
u_char *port_end;
unsigned http_minor:16;
unsigned http_major:16;

View file

@ -92,6 +92,13 @@ ngx_http_read_client_request_body(ngx_http_request_t *r,
}
#endif
#if (NGX_HTTP_V3)
if (r->http_version == NGX_HTTP_VERSION_30) {
rc = ngx_http_v3_read_request_body(r);
goto done;
}
#endif
preread = r->header_in->last - r->header_in->pos;
if (preread) {
@ -238,6 +245,18 @@ ngx_http_read_unbuffered_request_body(ngx_http_request_t *r)
}
#endif
#if (NGX_HTTP_V3)
if (r->http_version == NGX_HTTP_VERSION_30) {
rc = ngx_http_v3_read_unbuffered_request_body(r);
if (rc == NGX_OK) {
r->reading_body = 0;
}
return rc;
}
#endif
if (r->connection->read->timedout) {
r->connection->timedout = 1;
return NGX_HTTP_REQUEST_TIME_OUT;
@ -625,6 +644,12 @@ ngx_http_discard_request_body(ngx_http_request_t *r)
}
#endif
#if (NGX_HTTP_V3)
if (r->http_version == NGX_HTTP_VERSION_30) {
return NGX_OK;
}
#endif
if (ngx_http_test_expect(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
@ -920,6 +945,9 @@ ngx_http_test_expect(ngx_http_request_t *r)
|| r->http_version < NGX_HTTP_VERSION_11
#if (NGX_HTTP_V2)
|| r->stream != NULL
#endif
#if (NGX_HTTP_V3)
|| r->connection->quic != NULL
#endif
)
{

View file

@ -521,6 +521,13 @@ ngx_http_upstream_init(ngx_http_request_t *r)
}
#endif
#if (NGX_HTTP_V3)
if (c->quic) {
ngx_http_upstream_init_request(r);
return;
}
#endif
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
@ -1354,6 +1361,19 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
}
#endif
#if (NGX_HTTP_V3)
if (c->quic) {
if (c->write->error) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
}
return;
}
#endif
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
@ -3929,6 +3949,8 @@ ngx_http_upstream_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
r->aio = 1;
p->aio = 1;
ngx_add_timer(&task->event, 60000);
return NGX_OK;
}
@ -3947,6 +3969,17 @@ ngx_http_upstream_thread_event_handler(ngx_event_t *ev)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream thread: \"%V?%V\"", &r->uri, &r->args);
if (ev->timedout) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"thread operation took too long");
ev->timedout = 0;
return;
}
if (ev->timer_set) {
ngx_del_timer(ev);
}
r->main->blocked--;
r->aio = 0;
@ -3964,11 +3997,11 @@ ngx_http_upstream_thread_event_handler(ngx_event_t *ev)
#endif
if (r->done) {
if (r->done || r->main->terminated) {
/*
* trigger connection event handler if the subrequest was
* already finalized; this can happen if the handler is used
* for sendfile() in threads
* already finalized (this can happen if the handler is used
* for sendfile() in threads), or if the request was terminated
*/
c->write->handler(c->write);
@ -4541,6 +4574,10 @@ ngx_http_upstream_finalize_request(ngx_http_request_t *r,
u->peer.connection = NULL;
if (u->pipe) {
u->pipe->upstream = NULL;
}
if (u->pipe && u->pipe->temp_file) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream temp fd: %d",

View file

@ -828,7 +828,7 @@ ngx_http_variable_headers_internal(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data, u_char sep)
{
size_t len;
u_char *p;
u_char *p, *end;
ngx_table_elt_t *h, *th;
h = *(ngx_table_elt_t **) ((char *) r + data);
@ -870,6 +870,8 @@ ngx_http_variable_headers_internal(ngx_http_request_t *r,
v->len = len;
v->data = p;
end = p + len;
for (th = h; th; th = th->next) {
if (th->hash == 0) {
@ -878,7 +880,7 @@ ngx_http_variable_headers_internal(ngx_http_request_t *r,
p = ngx_copy(p, th->value.data, th->value.len);
if (th->next == NULL) {
if (p == end) {
break;
}

View file

@ -240,6 +240,10 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
r->out = NULL;
c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
if (last) {
r->response_sent = 1;
}
return NGX_OK;
}
@ -346,6 +350,10 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
if (last) {
r->response_sent = 1;
}
if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
return NGX_AGAIN;
}

View file

@ -11,14 +11,6 @@
#include <ngx_http_v2_module.h>
typedef struct {
ngx_str_t name;
ngx_uint_t offset;
ngx_uint_t hash;
ngx_http_header_t *hh;
} ngx_http_v2_parse_header_t;
/* errors */
#define NGX_HTTP_V2_NO_ERROR 0x0
#define NGX_HTTP_V2_PROTOCOL_ERROR 0x1
@ -63,8 +55,6 @@ static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c);
static void ngx_http_v2_lingering_close(ngx_connection_t *c);
static void ngx_http_v2_lingering_close_handler(ngx_event_t *rev);
static u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c,
@ -128,7 +118,7 @@ static ngx_int_t ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c,
u_char **pos, u_char *end, ngx_uint_t prefix);
static ngx_http_v2_stream_t *ngx_http_v2_create_stream(
ngx_http_v2_connection_t *h2c, ngx_uint_t push);
ngx_http_v2_connection_t *h2c);
static ngx_http_v2_node_t *ngx_http_v2_get_node_by_id(
ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t alloc);
static ngx_http_v2_node_t *ngx_http_v2_get_closed_node(
@ -164,14 +154,11 @@ static ngx_int_t ngx_http_v2_parse_scheme(ngx_http_request_t *r,
ngx_str_t *value);
static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r,
ngx_str_t *value);
static ngx_int_t ngx_http_v2_parse_header(ngx_http_request_t *r,
ngx_http_v2_parse_header_t *header, ngx_str_t *value);
static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r,
ngx_http_v2_header_t *header);
static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r);
static void ngx_http_v2_run_request(ngx_http_request_t *r);
static void ngx_http_v2_run_request_handler(ngx_event_t *ev);
static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r,
u_char *pos, size_t size, ngx_uint_t last, ngx_uint_t flush);
static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r);
@ -212,26 +199,10 @@ static ngx_http_v2_handler_pt ngx_http_v2_frame_states[] = {
(sizeof(ngx_http_v2_frame_states) / sizeof(ngx_http_v2_handler_pt))
static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = {
{ ngx_string("host"),
offsetof(ngx_http_headers_in_t, host), 0, NULL },
{ ngx_string("accept-encoding"),
offsetof(ngx_http_headers_in_t, accept_encoding), 0, NULL },
{ ngx_string("accept-language"),
offsetof(ngx_http_headers_in_t, accept_language), 0, NULL },
{ ngx_string("user-agent"),
offsetof(ngx_http_headers_in_t, user_agent), 0, NULL },
{ ngx_null_string, 0, 0, NULL }
};
void
ngx_http_v2_init(ngx_event_t *rev)
{
u_char *p, *end;
ngx_connection_t *c;
ngx_pool_cleanup_t *cln;
ngx_http_connection_t *hc;
@ -276,7 +247,6 @@ ngx_http_v2_init(ngx_event_t *rev)
h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);
h2c->concurrent_pushes = h2scf->concurrent_pushes;
h2c->priority_limit = ngx_max(h2scf->concurrent_streams, 100);
h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log);
@ -314,8 +284,7 @@ ngx_http_v2_init(ngx_event_t *rev)
return;
}
h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol
: ngx_http_v2_state_preface;
h2c->state.handler = ngx_http_v2_state_preface;
ngx_queue_init(&h2c->waiting);
ngx_queue_init(&h2c->dependencies);
@ -335,6 +304,23 @@ ngx_http_v2_init(ngx_event_t *rev)
c->idle = 1;
ngx_reusable_connection(c, 0);
if (c->buffer) {
p = c->buffer->pos;
end = c->buffer->last;
do {
p = h2c->state.handler(h2c, p, end);
if (p == NULL) {
return;
}
} while (p != end);
h2c->total_bytes += p - c->buffer->pos;
c->buffer->pos = p;
}
ngx_http_v2_read_handler(rev);
}
@ -361,6 +347,7 @@ ngx_http_v2_read_handler(ngx_event_t *rev)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 read handler");
h2c->blocked = 1;
h2c->new_streams = 0;
if (c->close) {
c->close = 0;
@ -370,7 +357,7 @@ ngx_http_v2_read_handler(ngx_event_t *rev)
return;
}
if (!h2c->processing && !h2c->pushing) {
if (!h2c->processing) {
ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);
return;
}
@ -399,13 +386,11 @@ ngx_http_v2_read_handler(ngx_event_t *rev)
h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE;
available = h2mcf->recv_buffer_size - NGX_HTTP_V2_STATE_BUFFER_SIZE;
do {
p = h2mcf->recv_buffer;
ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE);
end = p + h2c->state.buffer_used;
end = ngx_cpymem(p, h2c->state.buffer, h2c->state.buffer_used);
n = c->recv(c, end, available);
@ -413,9 +398,7 @@ ngx_http_v2_read_handler(ngx_event_t *rev)
break;
}
if (n == 0
&& (h2c->state.incomplete || h2c->processing || h2c->pushing))
{
if (n == 0 && (h2c->state.incomplete || h2c->processing)) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client prematurely closed connection");
}
@ -638,7 +621,7 @@ ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c)
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
if (h2c->last_out || h2c->processing || h2c->pushing) {
if (h2c->last_out || h2c->processing) {
return;
}
@ -846,32 +829,11 @@ ngx_http_v2_lingering_close_handler(ngx_event_t *rev)
}
static u_char *
ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
ngx_log_t *log;
log = h2c->connection->log;
log->action = "reading PROXY protocol";
pos = ngx_proxy_protocol_read(h2c->connection, pos, end);
log->action = "processing HTTP/2 connection";
if (pos == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
return ngx_http_v2_state_preface(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
static const u_char preface[] = "PRI * HTTP/2.0\r\n";
static const u_char preface[] = NGX_HTTP_V2_PREFACE_START;
if ((size_t) (end - pos) < sizeof(preface) - 1) {
return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_preface);
@ -892,7 +854,7 @@ static u_char *
ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
static const u_char preface[] = "\r\nSM\r\n\r\n";
static const u_char preface[] = NGX_HTTP_V2_PREFACE_END;
if ((size_t) (end - pos) < sizeof(preface) - 1) {
return ngx_http_v2_state_save(h2c, pos, end,
@ -1321,6 +1283,14 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
goto rst_stream;
}
if (h2c->new_streams++ >= 2 * h2scf->concurrent_streams) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent too many streams at once");
status = NGX_HTTP_V2_REFUSED_STREAM;
goto rst_stream;
}
if (!h2c->settings_ack
&& !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)
&& h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW)
@ -1344,7 +1314,7 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
h2c->closed_nodes--;
}
stream = ngx_http_v2_create_stream(h2c, 0);
stream = ngx_http_v2_create_stream(h2c);
if (stream == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
@ -1386,6 +1356,12 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
rst_stream:
if (h2c->refused_streams++ > ngx_max(h2scf->concurrent_streams, 100)) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent too many refused streams");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_NO_ERROR);
}
if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid, status) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
@ -2133,11 +2109,6 @@ ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, u_char *pos,
"client canceled stream %ui", h2c->state.sid);
break;
case NGX_HTTP_V2_REFUSED_STREAM:
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client refused stream %ui", h2c->state.sid);
break;
case NGX_HTTP_V2_INTERNAL_ERROR:
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client terminated stream %ui due to internal error",
@ -2205,7 +2176,6 @@ ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,
{
ssize_t window_delta;
ngx_uint_t id, value;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_out_frame_t *frame;
window_delta = 0;
@ -2267,14 +2237,6 @@ ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,
NGX_HTTP_V2_PROTOCOL_ERROR);
}
h2c->push_disabled = !value;
break;
case NGX_HTTP_V2_MAX_STREAMS_SETTING:
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
h2c->concurrent_pushes = ngx_min(value, h2scf->concurrent_pushes);
break;
case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:
@ -2628,7 +2590,7 @@ ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end,
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
ngx_memcpy(h2c->state.buffer, pos, NGX_HTTP_V2_STATE_BUFFER_SIZE);
ngx_memcpy(h2c->state.buffer, pos, size);
h2c->state.buffer_used = size;
h2c->state.handler = handler;
@ -2729,163 +2691,6 @@ ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c, u_char **pos, u_char *end,
}
ngx_http_v2_stream_t *
ngx_http_v2_push_stream(ngx_http_v2_stream_t *parent, ngx_str_t *path)
{
ngx_int_t rc;
ngx_str_t value;
ngx_pool_t *pool;
ngx_uint_t index;
ngx_table_elt_t **h;
ngx_connection_t *fc;
ngx_http_request_t *r;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_connection_t *h2c;
ngx_http_v2_parse_header_t *header;
h2c = parent->connection;
pool = ngx_create_pool(1024, h2c->connection->log);
if (pool == NULL) {
goto rst_stream;
}
node = ngx_http_v2_get_node_by_id(h2c, h2c->last_push, 1);
if (node == NULL) {
ngx_destroy_pool(pool);
goto rst_stream;
}
stream = ngx_http_v2_create_stream(h2c, 1);
if (stream == NULL) {
if (node->parent == NULL) {
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
index = ngx_http_v2_index(h2scf, h2c->last_push);
h2c->streams_index[index] = node->index;
ngx_queue_insert_tail(&h2c->closed, &node->reuse);
h2c->closed_nodes++;
}
ngx_destroy_pool(pool);
goto rst_stream;
}
if (node->parent) {
ngx_queue_remove(&node->reuse);
h2c->closed_nodes--;
}
stream->pool = pool;
r = stream->request;
fc = r->connection;
stream->in_closed = 1;
stream->node = node;
node->stream = stream;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 push stream sid:%ui "
"depends on %ui excl:0 weight:16",
h2c->last_push, parent->node->id);
node->weight = NGX_HTTP_V2_DEFAULT_WEIGHT;
ngx_http_v2_set_dependency(h2c, node, parent->node->id, 0);
r->method_name = ngx_http_core_get_method;
r->method = NGX_HTTP_GET;
r->schema.data = ngx_pstrdup(pool, &parent->request->schema);
if (r->schema.data == NULL) {
goto close;
}
r->schema.len = parent->request->schema.len;
value.data = ngx_pstrdup(pool, path);
if (value.data == NULL) {
goto close;
}
value.len = path->len;
rc = ngx_http_v2_parse_path(r, &value);
if (rc != NGX_OK) {
goto error;
}
for (header = ngx_http_v2_parse_headers; header->name.len; header++) {
h = (ngx_table_elt_t **)
((char *) &parent->request->headers_in + header->offset);
if (*h == NULL) {
continue;
}
value.len = (*h)->value.len;
value.data = ngx_pnalloc(pool, value.len + 1);
if (value.data == NULL) {
goto close;
}
ngx_memcpy(value.data, (*h)->value.data, value.len);
value.data[value.len] = '\0';
rc = ngx_http_v2_parse_header(r, header, &value);
if (rc != NGX_OK) {
goto error;
}
}
fc->write->handler = ngx_http_v2_run_request_handler;
ngx_post_event(fc->write, &ngx_posted_events);
return stream;
error:
if (rc == NGX_ABORT) {
/* header handler has already finalized request */
ngx_http_run_posted_requests(fc);
return NULL;
}
if (rc == NGX_DECLINED) {
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
ngx_http_run_posted_requests(fc);
return NULL;
}
close:
ngx_http_v2_close_stream(stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NULL;
rst_stream:
if (ngx_http_v2_send_rst_stream(h2c, h2c->last_push,
NGX_HTTP_INTERNAL_SERVER_ERROR)
!= NGX_OK)
{
h2c->connection->error = 1;
}
return NULL;
}
static ngx_int_t
ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c)
{
@ -3157,7 +2962,7 @@ ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c,
static ngx_http_v2_stream_t *
ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t push)
ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c)
{
ngx_log_t *log;
ngx_event_t *rev, *wev;
@ -3212,13 +3017,7 @@ ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t push)
ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t));
log->data = ctx;
if (push) {
log->action = "processing pushed request headers";
} else {
log->action = "reading client request headers";
}
log->action = "reading client request headers";
ngx_memzero(rev, sizeof(ngx_event_t));
@ -3290,12 +3089,7 @@ ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t push)
stream->send_window = h2c->init_window;
stream->recv_window = h2scf->preread_size;
if (push) {
h2c->pushing++;
} else {
h2c->processing++;
}
h2c->processing++;
h2c->priority_limit += h2scf->concurrent_streams;
@ -3717,46 +3511,42 @@ ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)
static ngx_int_t
ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value)
{
return ngx_http_v2_parse_header(r, &ngx_http_v2_parse_headers[0], value);
}
static ngx_int_t
ngx_http_v2_parse_header(ngx_http_request_t *r,
ngx_http_v2_parse_header_t *header, ngx_str_t *value)
{
ngx_table_elt_t *h;
ngx_http_header_t *hh;
ngx_http_core_main_conf_t *cmcf;
static ngx_str_t host = ngx_string("host");
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->key.len = header->name.len;
h->key.data = header->name.data;
h->lowcase_key = header->name.data;
h->hash = ngx_hash(ngx_hash(ngx_hash('h', 'o'), 's'), 't');
if (header->hh == NULL) {
header->hash = ngx_hash_key(header->name.data, header->name.len);
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
header->hh = ngx_hash_find(&cmcf->headers_in_hash, header->hash,
h->lowcase_key, h->key.len);
if (header->hh == NULL) {
return NGX_ERROR;
}
}
h->hash = header->hash;
h->key.len = host.len;
h->key.data = host.data;
h->value.len = value->len;
h->value.data = value->data;
if (header->hh->handler(r, h, header->hh->offset) != NGX_OK) {
/* header handler has already finalized request */
h->lowcase_key = host.data;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh == NULL) {
return NGX_ERROR;
}
if (hh->handler(r, h, hh->offset) != NGX_OK) {
/*
* request has been finalized already
* in ngx_http_process_host()
*/
return NGX_ABORT;
}
@ -3943,10 +3733,22 @@ static void
ngx_http_v2_run_request(ngx_http_request_t *r)
{
ngx_connection_t *fc;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_connection_t *h2c;
fc = r->connection;
h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
if (!h2scf->enable && !r->http_connection->addr_conf->http2) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client attempted to request the server name "
"for which the negotiated protocol is disabled");
ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST);
goto failed;
}
if (ngx_http_v2_construct_request_line(r) != NGX_OK) {
goto failed;
}
@ -3987,22 +3789,6 @@ failed:
}
static void
ngx_http_v2_run_request_handler(ngx_event_t *ev)
{
ngx_connection_t *fc;
ngx_http_request_t *r;
fc = ev->data;
r = fc->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 run request handler");
ngx_http_v2_run_request(r);
}
ngx_int_t
ngx_http_v2_read_request_body(ngx_http_request_t *r)
{
@ -4606,7 +4392,6 @@ void
ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)
{
ngx_pool_t *pool;
ngx_uint_t push;
ngx_event_t *ev;
ngx_connection_t *fc;
ngx_http_v2_node_t *node;
@ -4615,10 +4400,9 @@ ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)
h2c = stream->connection;
node = stream->node;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 close stream %ui, queued %ui, "
"processing %ui, pushing %ui",
node->id, stream->queued, h2c->processing, h2c->pushing);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 close stream %ui, queued %ui, processing %ui",
node->id, stream->queued, h2c->processing);
fc = stream->request->connection;
@ -4653,8 +4437,6 @@ ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)
h2c->state.stream = NULL;
}
push = stream->node->id % 2 == 0;
node->stream = NULL;
ngx_queue_insert_tail(&h2c->closed, &node->reuse);
@ -4704,14 +4486,9 @@ ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)
fc->data = h2c->free_fake_connections;
h2c->free_fake_connections = fc;
if (push) {
h2c->pushing--;
h2c->processing--;
} else {
h2c->processing--;
}
if (h2c->processing || h2c->pushing || h2c->blocked) {
if (h2c->processing || h2c->blocked) {
return;
}
@ -4887,7 +4664,7 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
}
}
if (!h2c->processing && !h2c->pushing) {
if (!h2c->processing) {
goto done;
}
@ -4935,7 +4712,7 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
h2c->blocked = 0;
if (h2c->processing || h2c->pushing) {
if (h2c->processing) {
c->error = 1;
return;
}

View file

@ -24,8 +24,6 @@
#define NGX_HTTP_V2_MAX_FIELD \
(127 + (1 << (NGX_HTTP_V2_INT_OCTETS - 1) * 7) - 1)
#define NGX_HTTP_V2_STREAM_ID_SIZE 4
#define NGX_HTTP_V2_FRAME_HEADER_SIZE 9
/* frame types */
@ -63,6 +61,15 @@ typedef u_char *(*ngx_http_v2_handler_pt) (ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
typedef struct {
ngx_flag_t enable;
size_t pool_size;
ngx_uint_t concurrent_streams;
size_t preread_size;
ngx_uint_t streams_index_mask;
} ngx_http_v2_srv_conf_t;
typedef struct {
ngx_str_t name;
ngx_str_t value;
@ -124,11 +131,10 @@ struct ngx_http_v2_connection_s {
ngx_uint_t processing;
ngx_uint_t frames;
ngx_uint_t idle;
ngx_uint_t new_streams;
ngx_uint_t refused_streams;
ngx_uint_t priority_limit;
ngx_uint_t pushing;
ngx_uint_t concurrent_pushes;
size_t send_window;
size_t recv_window;
size_t init_window;
@ -155,7 +161,6 @@ struct ngx_http_v2_connection_s {
ngx_uint_t closed_nodes;
ngx_uint_t last_sid;
ngx_uint_t last_push;
time_t lingering_time;
@ -163,7 +168,6 @@ struct ngx_http_v2_connection_s {
unsigned table_update:1;
unsigned blocked:1;
unsigned goaway:1;
unsigned push_disabled:1;
};
@ -293,9 +297,6 @@ void ngx_http_v2_init(ngx_event_t *rev);
ngx_int_t ngx_http_v2_read_request_body(ngx_http_request_t *r);
ngx_int_t ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r);
ngx_http_v2_stream_t *ngx_http_v2_push_stream(ngx_http_v2_stream_t *parent,
ngx_str_t *path);
void ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc);
ngx_int_t ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c);
@ -397,20 +398,25 @@ ngx_int_t ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size);
#define NGX_HTTP_V2_STATUS_404_INDEX 13
#define NGX_HTTP_V2_STATUS_500_INDEX 14
#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16
#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17
#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28
#define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31
#define NGX_HTTP_V2_DATE_INDEX 33
#define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44
#define NGX_HTTP_V2_LOCATION_INDEX 46
#define NGX_HTTP_V2_SERVER_INDEX 54
#define NGX_HTTP_V2_USER_AGENT_INDEX 58
#define NGX_HTTP_V2_VARY_INDEX 59
#define NGX_HTTP_V2_PREFACE_START "PRI * HTTP/2.0\r\n"
#define NGX_HTTP_V2_PREFACE_END "\r\nSM\r\n\r\n"
#define NGX_HTTP_V2_PREFACE NGX_HTTP_V2_PREFACE_START \
NGX_HTTP_V2_PREFACE_END
u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,
u_char *tmp, ngx_uint_t lower);
extern ngx_module_t ngx_http_v2_module;
#endif /* _NGX_HTTP_V2_H_INCLUDED_ */

View file

@ -27,39 +27,8 @@
#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1
typedef struct {
ngx_str_t name;
u_char index;
ngx_uint_t offset;
} ngx_http_v2_push_header_t;
static ngx_http_v2_push_header_t ngx_http_v2_push_headers[] = {
{ ngx_string(":authority"), NGX_HTTP_V2_AUTHORITY_INDEX,
offsetof(ngx_http_headers_in_t, host) },
{ ngx_string("accept-encoding"), NGX_HTTP_V2_ACCEPT_ENCODING_INDEX,
offsetof(ngx_http_headers_in_t, accept_encoding) },
{ ngx_string("accept-language"), NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX,
offsetof(ngx_http_headers_in_t, accept_language) },
{ ngx_string("user-agent"), NGX_HTTP_V2_USER_AGENT_INDEX,
offsetof(ngx_http_headers_in_t, user_agent) },
};
#define NGX_HTTP_V2_PUSH_HEADERS \
(sizeof(ngx_http_v2_push_headers) / sizeof(ngx_http_v2_push_header_t))
static ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r,
ngx_str_t *path, ngx_str_t *binary);
static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(
ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin);
static ngx_http_v2_out_frame_t *ngx_http_v2_create_push_frame(
ngx_http_request_t *r, u_char *pos, u_char *end);
static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame(
ngx_http_request_t *r);
@ -82,8 +51,6 @@ static ngx_inline ngx_int_t ngx_http_v2_filter_send(
static ngx_int_t ngx_http_v2_headers_frame_handler(
ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
static ngx_int_t ngx_http_v2_push_frame_handler(
ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
static ngx_int_t ngx_http_v2_data_frame_handler(
ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
static ngx_inline void ngx_http_v2_handle_frame(
@ -244,15 +211,6 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
h2c = stream->connection;
if (!h2c->push_disabled && !h2c->goaway
&& stream->node->id % 2 == 1
&& r->method != NGX_HTTP_HEAD)
{
if (ngx_http_v2_push_resources(r) != NGX_OK) {
return NGX_ERROR;
}
}
len = h2c->table_update ? 1 : 0;
len += status ? 1 : 1 + ngx_http_v2_literal_size("418");
@ -653,7 +611,7 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
ngx_http_v2_queue_blocked_frame(h2c, frame);
stream->queued++;
stream->queued = 1;
cln = ngx_http_cleanup_add(r, 0);
if (cln == NULL) {
@ -671,409 +629,6 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
}
static ngx_int_t
ngx_http_v2_push_resources(ngx_http_request_t *r)
{
u_char *start, *end, *last;
ngx_int_t rc;
ngx_str_t path;
ngx_uint_t i, push;
ngx_table_elt_t *h;
ngx_http_v2_loc_conf_t *h2lcf;
ngx_http_complex_value_t *pushes;
ngx_str_t binary[NGX_HTTP_V2_PUSH_HEADERS];
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2 push resources");
ngx_memzero(binary, NGX_HTTP_V2_PUSH_HEADERS * sizeof(ngx_str_t));
h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
if (h2lcf->pushes) {
pushes = h2lcf->pushes->elts;
for (i = 0; i < h2lcf->pushes->nelts; i++) {
if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) {
return NGX_ERROR;
}
if (path.len == 0) {
continue;
}
if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) {
continue;
}
rc = ngx_http_v2_push_resource(r, &path, binary);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_ABORT) {
return NGX_OK;
}
/* NGX_OK, NGX_DECLINED */
}
}
if (!h2lcf->push_preload) {
return NGX_OK;
}
for (h = r->headers_out.link; h; h = h->next) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2 parse link: \"%V\"", &h->value);
start = h->value.data;
end = h->value.data + h->value.len;
next_link:
while (start < end && *start == ' ') { start++; }
if (start == end || *start++ != '<') {
continue;
}
while (start < end && *start == ' ') { start++; }
for (last = start; last < end && *last != '>'; last++) {
/* void */
}
if (last == start || last == end) {
continue;
}
path.len = last - start;
path.data = start;
start = last + 1;
while (start < end && *start == ' ') { start++; }
if (start == end) {
continue;
}
if (*start == ',') {
start++;
goto next_link;
}
if (*start++ != ';') {
continue;
}
last = ngx_strlchr(start, end, ',');
if (last == NULL) {
last = end;
}
push = 0;
for ( ;; ) {
while (start < last && *start == ' ') { start++; }
if (last - start >= 6
&& ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0)
{
start += 6;
if (start == last || *start == ' ' || *start == ';') {
push = 0;
break;
}
goto next_param;
}
if (last - start >= 11
&& ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0)
{
start += 11;
if (start == last || *start == ' ' || *start == ';') {
push = 1;
}
goto next_param;
}
if (last - start >= 4
&& ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0)
{
start += 4;
while (start < last && *start == ' ') { start++; }
if (start == last || *start++ != '"') {
goto next_param;
}
for ( ;; ) {
while (start < last && *start == ' ') { start++; }
if (last - start >= 7
&& ngx_strncasecmp(start, (u_char *) "preload", 7) == 0)
{
start += 7;
if (start < last && (*start == ' ' || *start == '"')) {
push = 1;
break;
}
}
while (start < last && *start != ' ' && *start != '"') {
start++;
}
if (start == last) {
break;
}
if (*start == '"') {
break;
}
start++;
}
}
next_param:
start = ngx_strlchr(start, last, ';');
if (start == NULL) {
break;
}
start++;
}
if (push) {
while (path.len && path.data[path.len - 1] == ' ') {
path.len--;
}
}
if (push && path.len
&& !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/'))
{
rc = ngx_http_v2_push_resource(r, &path, binary);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_ABORT) {
return NGX_OK;
}
/* NGX_OK, NGX_DECLINED */
}
if (last < end) {
start = last + 1;
goto next_link;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
ngx_str_t *binary)
{
u_char *start, *pos, *tmp;
size_t len;
ngx_str_t *value;
ngx_uint_t i;
ngx_table_elt_t **h;
ngx_connection_t *fc;
ngx_http_v2_stream_t *stream;
ngx_http_v2_out_frame_t *frame;
ngx_http_v2_connection_t *h2c;
ngx_http_v2_push_header_t *ph;
fc = r->connection;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 push resource");
stream = r->stream;
h2c = stream->connection;
if (!ngx_path_separator(path->data[0])) {
ngx_log_error(NGX_LOG_WARN, fc->log, 0,
"non-absolute path \"%V\" not pushed", path);
return NGX_DECLINED;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 pushing:%ui limit:%ui",
h2c->pushing, h2c->concurrent_pushes);
if (h2c->pushing >= h2c->concurrent_pushes) {
return NGX_ABORT;
}
if (h2c->last_push == 0x7ffffffe) {
return NGX_ABORT;
}
if (path->len > NGX_HTTP_V2_MAX_FIELD) {
return NGX_DECLINED;
}
if (r->headers_in.host == NULL) {
return NGX_ABORT;
}
ph = ngx_http_v2_push_headers;
len = ngx_max(r->schema.len, path->len);
if (binary[0].len) {
tmp = ngx_palloc(r->pool, len);
if (tmp == NULL) {
return NGX_ERROR;
}
} else {
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
if (*h) {
len = ngx_max(len, (*h)->value.len);
}
}
tmp = ngx_palloc(r->pool, len);
if (tmp == NULL) {
return NGX_ERROR;
}
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
if (*h == NULL) {
continue;
}
value = &(*h)->value;
len = 1 + NGX_HTTP_V2_INT_OCTETS + value->len;
pos = ngx_pnalloc(r->pool, len);
if (pos == NULL) {
return NGX_ERROR;
}
binary[i].data = pos;
*pos++ = ngx_http_v2_inc_indexed(ph[i].index);
pos = ngx_http_v2_write_value(pos, value->data, value->len, tmp);
binary[i].len = pos - binary[i].data;
}
}
len = (h2c->table_update ? 1 : 0)
+ 1
+ 1 + NGX_HTTP_V2_INT_OCTETS + path->len
+ 1 + NGX_HTTP_V2_INT_OCTETS + r->schema.len;
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
len += binary[i].len;
}
pos = ngx_pnalloc(r->pool, len);
if (pos == NULL) {
return NGX_ERROR;
}
start = pos;
if (h2c->table_update) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 table size update: 0");
*pos++ = (1 << 5) | 0;
h2c->table_update = 0;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 push header: \":method: GET\"");
*pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 push header: \":path: %V\"", path);
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 push header: \":scheme: %V\"", &r->schema);
if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) {
*pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
} else if (r->schema.len == 4
&& ngx_strncmp(r->schema.data, "http", 4) == 0)
{
*pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
} else {
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
pos = ngx_http_v2_write_value(pos, r->schema.data, r->schema.len, tmp);
}
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
if (*h == NULL) {
continue;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 push header: \"%V: %V\"",
&ph[i].name, &(*h)->value);
pos = ngx_cpymem(pos, binary[i].data, binary[i].len);
}
frame = ngx_http_v2_create_push_frame(r, start, pos);
if (frame == NULL) {
return NGX_ERROR;
}
ngx_http_v2_queue_blocked_frame(h2c, frame);
stream->queued++;
stream = ngx_http_v2_push_stream(stream, path);
if (stream) {
stream->request->request_length = pos - start;
return NGX_OK;
}
return NGX_ERROR;
}
static ngx_http_v2_out_frame_t *
ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
u_char *end, ngx_uint_t fin)
@ -1179,125 +734,6 @@ ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
}
static ngx_http_v2_out_frame_t *
ngx_http_v2_create_push_frame(ngx_http_request_t *r, u_char *pos, u_char *end)
{
u_char type, flags;
size_t rest, frame_size, len;
ngx_buf_t *b;
ngx_chain_t *cl, **ll;
ngx_http_v2_stream_t *stream;
ngx_http_v2_out_frame_t *frame;
ngx_http_v2_connection_t *h2c;
stream = r->stream;
h2c = stream->connection;
rest = NGX_HTTP_V2_STREAM_ID_SIZE + (end - pos);
frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
if (frame == NULL) {
return NULL;
}
frame->handler = ngx_http_v2_push_frame_handler;
frame->stream = stream;
frame->length = rest;
frame->blocked = 1;
frame->fin = 0;
ll = &frame->first;
type = NGX_HTTP_V2_PUSH_PROMISE_FRAME;
flags = NGX_HTTP_V2_NO_FLAG;
frame_size = h2c->frame_size;
for ( ;; ) {
if (rest <= frame_size) {
frame_size = rest;
flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
}
b = ngx_create_temp_buf(r->pool,
NGX_HTTP_V2_FRAME_HEADER_SIZE
+ ((type == NGX_HTTP_V2_PUSH_PROMISE_FRAME)
? NGX_HTTP_V2_STREAM_ID_SIZE : 0));
if (b == NULL) {
return NULL;
}
b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
*b->last++ = flags;
b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
if (type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
h2c->last_push += 2;
b->last = ngx_http_v2_write_sid(b->last, h2c->last_push);
len = frame_size - NGX_HTTP_V2_STREAM_ID_SIZE;
} else {
len = frame_size;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NULL;
}
cl->buf = b;
*ll = cl;
ll = &cl->next;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NULL;
}
b->pos = pos;
pos += len;
b->last = pos;
b->start = b->pos;
b->end = b->last;
b->temporary = 1;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NULL;
}
cl->buf = b;
*ll = cl;
ll = &cl->next;
rest -= frame_size;
if (rest) {
frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
type = NGX_HTTP_V2_CONTINUATION_FRAME;
continue;
}
cl->next = NULL;
frame->last = cl;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2:%ui create PUSH_PROMISE frame %p: "
"sid:%ui len:%uz",
stream->node->id, frame, h2c->last_push,
frame->length);
return frame;
}
}
static ngx_http_v2_out_frame_t *
ngx_http_v2_create_trailers_frame(ngx_http_request_t *r)
{
@ -1901,62 +1337,6 @@ ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,
}
static ngx_int_t
ngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)
{
ngx_chain_t *cl, *ln;
ngx_http_v2_stream_t *stream;
stream = frame->stream;
cl = frame->first;
for ( ;; ) {
if (cl->buf->pos != cl->buf->last) {
frame->first = cl;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui PUSH_PROMISE frame %p was sent partially",
stream->node->id, frame);
return NGX_AGAIN;
}
ln = cl->next;
if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
cl->next = stream->free_frame_headers;
stream->free_frame_headers = cl;
} else {
cl->next = stream->free_bufs;
stream->free_bufs = cl;
}
if (cl == frame->last) {
break;
}
cl = ln;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui PUSH_PROMISE frame %p was sent",
stream->node->id, frame);
stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
+ frame->length;
h2c->payload_bytes += frame->length;
ngx_http_v2_handle_frame(stream, frame);
ngx_http_v2_handle_stream(h2c, stream);
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)

View file

@ -27,8 +27,6 @@ static void *ngx_http_v2_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post,
void *data);
static char *ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data);
@ -75,6 +73,13 @@ static ngx_conf_post_t ngx_http_v2_chunk_size_post =
static ngx_command_t ngx_http_v2_commands[] = {
{ ngx_string("http2"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, enable),
NULL },
{ ngx_string("http2_recv_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
@ -98,9 +103,9 @@ static ngx_command_t ngx_http_v2_commands[] = {
{ ngx_string("http2_max_concurrent_pushes"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, concurrent_pushes),
ngx_http_v2_obsolete,
0,
0,
NULL },
{ ngx_string("http2_max_requests"),
@ -161,15 +166,15 @@ static ngx_command_t ngx_http_v2_commands[] = {
{ ngx_string("http2_push_preload"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_v2_loc_conf_t, push_preload),
ngx_http_v2_obsolete,
0,
0,
NULL },
{ ngx_string("http2_push"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_v2_push,
NGX_HTTP_LOC_CONF_OFFSET,
ngx_http_v2_obsolete,
0,
0,
NULL },
@ -314,10 +319,11 @@ ngx_http_v2_create_srv_conf(ngx_conf_t *cf)
return NULL;
}
h2scf->enable = NGX_CONF_UNSET;
h2scf->pool_size = NGX_CONF_UNSET_SIZE;
h2scf->concurrent_streams = NGX_CONF_UNSET_UINT;
h2scf->concurrent_pushes = NGX_CONF_UNSET_UINT;
h2scf->preread_size = NGX_CONF_UNSET_SIZE;
@ -333,12 +339,12 @@ ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_http_v2_srv_conf_t *prev = parent;
ngx_http_v2_srv_conf_t *conf = child;
ngx_conf_merge_value(conf->enable, prev->enable, 0);
ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);
ngx_conf_merge_uint_value(conf->concurrent_streams,
prev->concurrent_streams, 128);
ngx_conf_merge_uint_value(conf->concurrent_pushes,
prev->concurrent_pushes, 10);
ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536);
@ -359,17 +365,8 @@ ngx_http_v2_create_loc_conf(ngx_conf_t *cf)
return NULL;
}
/*
* set by ngx_pcalloc():
*
* h2lcf->pushes = NULL;
*/
h2lcf->chunk_size = NGX_CONF_UNSET_SIZE;
h2lcf->push_preload = NGX_CONF_UNSET;
h2lcf->push = NGX_CONF_UNSET;
return h2lcf;
}
@ -382,72 +379,6 @@ ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);
ngx_conf_merge_value(conf->push, prev->push, 1);
if (conf->push && conf->pushes == NULL) {
conf->pushes = prev->pushes;
}
ngx_conf_merge_value(conf->push_preload, prev->push_preload, 0);
return NGX_CONF_OK;
}
static char *
ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_v2_loc_conf_t *h2lcf = conf;
ngx_str_t *value;
ngx_http_complex_value_t *cv;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "off") == 0) {
if (h2lcf->pushes) {
return "\"off\" parameter cannot be used with URI";
}
if (h2lcf->push == 0) {
return "is duplicate";
}
h2lcf->push = 0;
return NGX_CONF_OK;
}
if (h2lcf->push == 0) {
return "URI cannot be used with \"off\" parameter";
}
h2lcf->push = 1;
if (h2lcf->pushes == NULL) {
h2lcf->pushes = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_complex_value_t));
if (h2lcf->pushes == NULL) {
return NGX_CONF_ERROR;
}
}
cv = ngx_array_push(h2lcf->pushes);
if (cv == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
@ -457,7 +388,7 @@ ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)
{
size_t *sp = data;
if (*sp <= 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE) {
if (*sp <= NGX_HTTP_V2_STATE_BUFFER_SIZE) {
return "value is too small";
}
@ -551,10 +482,17 @@ ngx_http_v2_obsolete(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_conf_deprecated_t *d = cmd->post;
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"the \"%s\" directive is obsolete, "
"use the \"%s\" directive instead",
d->old_name, d->new_name);
if (d) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"the \"%s\" directive is obsolete, "
"use the \"%s\" directive instead",
d->old_name, d->new_name);
} else {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"the \"%V\" directive is obsolete, ignored",
&cmd->name);
}
return NGX_CONF_OK;
}

View file

@ -20,26 +20,9 @@ typedef struct {
} ngx_http_v2_main_conf_t;
typedef struct {
size_t pool_size;
ngx_uint_t concurrent_streams;
ngx_uint_t concurrent_pushes;
size_t preread_size;
ngx_uint_t streams_index_mask;
} ngx_http_v2_srv_conf_t;
typedef struct {
size_t chunk_size;
ngx_flag_t push_preload;
ngx_flag_t push;
ngx_array_t *pushes;
} ngx_http_v2_loc_conf_t;
extern ngx_module_t ngx_http_v2_module;
#endif /* _NGX_HTTP_V2_MODULE_H_INCLUDED_ */

Some files were not shown because too many files have changed in this diff Show more