protocol-quic 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/ext/ngtcp2/AUTHORS +44 -0
- data/ext/ngtcp2/CMakeLists.txt +431 -0
- data/ext/ngtcp2/CMakeOptions.txt +17 -0
- data/ext/ngtcp2/COPYING +22 -0
- data/ext/ngtcp2/ChangeLog +0 -0
- data/ext/ngtcp2/Makefile.am +60 -0
- data/ext/ngtcp2/NEWS +0 -0
- data/ext/ngtcp2/README +1 -0
- data/ext/ngtcp2/README.rst +258 -0
- data/ext/ngtcp2/ci/build_boringssl.sh +10 -0
- data/ext/ngtcp2/ci/build_nghttp3.sh +9 -0
- data/ext/ngtcp2/ci/build_openssl1.sh +8 -0
- data/ext/ngtcp2/ci/build_openssl1_cross.sh +9 -0
- data/ext/ngtcp2/ci/build_openssl3.sh +8 -0
- data/ext/ngtcp2/ci/build_picotls.sh +26 -0
- data/ext/ngtcp2/ci/build_wolfssl.sh +9 -0
- data/ext/ngtcp2/ci/gen-certificate.sh +8 -0
- data/ext/ngtcp2/cmake/ExtractValidFlags.cmake +31 -0
- data/ext/ngtcp2/cmake/FindCUnit.cmake +40 -0
- data/ext/ngtcp2/cmake/FindJemalloc.cmake +40 -0
- data/ext/ngtcp2/cmake/FindLibev.cmake +38 -0
- data/ext/ngtcp2/cmake/FindLibnghttp3.cmake +41 -0
- data/ext/ngtcp2/cmake/Findwolfssl.cmake +41 -0
- data/ext/ngtcp2/cmake/Version.cmake +11 -0
- data/ext/ngtcp2/cmakeconfig.h.in +36 -0
- data/ext/ngtcp2/configure.ac +755 -0
- data/ext/ngtcp2/crypto/CMakeLists.txt +56 -0
- data/ext/ngtcp2/crypto/Makefile.am +49 -0
- data/ext/ngtcp2/crypto/boringssl/CMakeLists.txt +64 -0
- data/ext/ngtcp2/crypto/boringssl/Makefile.am +39 -0
- data/ext/ngtcp2/crypto/boringssl/boringssl.c +630 -0
- data/ext/ngtcp2/crypto/boringssl/libngtcp2_crypto_boringssl.pc.in +33 -0
- data/ext/ngtcp2/crypto/gnutls/CMakeLists.txt +86 -0
- data/ext/ngtcp2/crypto/gnutls/Makefile.am +43 -0
- data/ext/ngtcp2/crypto/gnutls/gnutls.c +644 -0
- data/ext/ngtcp2/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in +33 -0
- data/ext/ngtcp2/crypto/includes/CMakeLists.txt +56 -0
- data/ext/ngtcp2/crypto/includes/Makefile.am +45 -0
- data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h +893 -0
- data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h +104 -0
- data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h +107 -0
- data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h +132 -0
- data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h +246 -0
- data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h +106 -0
- data/ext/ngtcp2/crypto/openssl/CMakeLists.txt +86 -0
- data/ext/ngtcp2/crypto/openssl/Makefile.am +43 -0
- data/ext/ngtcp2/crypto/openssl/libngtcp2_crypto_openssl.pc.in +33 -0
- data/ext/ngtcp2/crypto/openssl/openssl.c +807 -0
- data/ext/ngtcp2/crypto/picotls/CMakeLists.txt +65 -0
- data/ext/ngtcp2/crypto/picotls/Makefile.am +39 -0
- data/ext/ngtcp2/crypto/picotls/libngtcp2_crypto_picotls.pc.in +33 -0
- data/ext/ngtcp2/crypto/picotls/picotls.c +707 -0
- data/ext/ngtcp2/crypto/shared.c +1431 -0
- data/ext/ngtcp2/crypto/shared.h +350 -0
- data/ext/ngtcp2/crypto/wolfssl/CMakeLists.txt +84 -0
- data/ext/ngtcp2/crypto/wolfssl/Makefile.am +43 -0
- data/ext/ngtcp2/crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in +33 -0
- data/ext/ngtcp2/crypto/wolfssl/wolfssl.c +534 -0
- data/ext/ngtcp2/doc/Makefile.am +65 -0
- data/ext/ngtcp2/doc/make.bat +35 -0
- data/ext/ngtcp2/doc/mkapiref.py +356 -0
- data/ext/ngtcp2/doc/source/conf.py.in +94 -0
- data/ext/ngtcp2/doc/source/index.rst +22 -0
- data/ext/ngtcp2/doc/source/programmers-guide.rst +476 -0
- data/ext/ngtcp2/docker/Dockerfile +39 -0
- data/ext/ngtcp2/examples/CMakeLists.txt +361 -0
- data/ext/ngtcp2/examples/Makefile.am +228 -0
- data/ext/ngtcp2/examples/client.cc +3049 -0
- data/ext/ngtcp2/examples/client.h +192 -0
- data/ext/ngtcp2/examples/client_base.cc +202 -0
- data/ext/ngtcp2/examples/client_base.h +213 -0
- data/ext/ngtcp2/examples/debug.cc +298 -0
- data/ext/ngtcp2/examples/debug.h +124 -0
- data/ext/ngtcp2/examples/examplestest.cc +84 -0
- data/ext/ngtcp2/examples/gtlssimpleclient.c +720 -0
- data/ext/ngtcp2/examples/h09client.cc +2601 -0
- data/ext/ngtcp2/examples/h09client.h +196 -0
- data/ext/ngtcp2/examples/h09server.cc +3024 -0
- data/ext/ngtcp2/examples/h09server.h +237 -0
- data/ext/ngtcp2/examples/http.cc +138 -0
- data/ext/ngtcp2/examples/http.h +44 -0
- data/ext/ngtcp2/examples/network.h +80 -0
- data/ext/ngtcp2/examples/server.cc +3731 -0
- data/ext/ngtcp2/examples/server.h +256 -0
- data/ext/ngtcp2/examples/server_base.cc +58 -0
- data/ext/ngtcp2/examples/server_base.h +195 -0
- data/ext/ngtcp2/examples/shared.cc +385 -0
- data/ext/ngtcp2/examples/shared.h +96 -0
- data/ext/ngtcp2/examples/simpleclient.c +683 -0
- data/ext/ngtcp2/examples/template.h +71 -0
- data/ext/ngtcp2/examples/tests/README.rst +60 -0
- data/ext/ngtcp2/examples/tests/__init__.py +0 -0
- data/ext/ngtcp2/examples/tests/config.ini.in +32 -0
- data/ext/ngtcp2/examples/tests/conftest.py +28 -0
- data/ext/ngtcp2/examples/tests/ngtcp2test/__init__.py +6 -0
- data/ext/ngtcp2/examples/tests/ngtcp2test/certs.py +476 -0
- data/ext/ngtcp2/examples/tests/ngtcp2test/client.py +187 -0
- data/ext/ngtcp2/examples/tests/ngtcp2test/env.py +191 -0
- data/ext/ngtcp2/examples/tests/ngtcp2test/log.py +101 -0
- data/ext/ngtcp2/examples/tests/ngtcp2test/server.py +137 -0
- data/ext/ngtcp2/examples/tests/ngtcp2test/tls.py +983 -0
- data/ext/ngtcp2/examples/tests/test_01_handshake.py +30 -0
- data/ext/ngtcp2/examples/tests/test_02_resume.py +46 -0
- data/ext/ngtcp2/examples/tests/test_03_earlydata.py +56 -0
- data/ext/ngtcp2/examples/tests/test_04_clientcert.py +57 -0
- data/ext/ngtcp2/examples/tests/test_05_ciphers.py +46 -0
- data/ext/ngtcp2/examples/tls_client_context.h +52 -0
- data/ext/ngtcp2/examples/tls_client_context_boringssl.cc +126 -0
- data/ext/ngtcp2/examples/tls_client_context_boringssl.h +49 -0
- data/ext/ngtcp2/examples/tls_client_context_gnutls.cc +74 -0
- data/ext/ngtcp2/examples/tls_client_context_gnutls.h +50 -0
- data/ext/ngtcp2/examples/tls_client_context_openssl.cc +137 -0
- data/ext/ngtcp2/examples/tls_client_context_openssl.h +49 -0
- data/ext/ngtcp2/examples/tls_client_context_picotls.cc +158 -0
- data/ext/ngtcp2/examples/tls_client_context_picotls.h +53 -0
- data/ext/ngtcp2/examples/tls_client_context_wolfssl.cc +177 -0
- data/ext/ngtcp2/examples/tls_client_context_wolfssl.h +51 -0
- data/ext/ngtcp2/examples/tls_client_session.h +52 -0
- data/ext/ngtcp2/examples/tls_client_session_boringssl.cc +110 -0
- data/ext/ngtcp2/examples/tls_client_session_boringssl.h +52 -0
- data/ext/ngtcp2/examples/tls_client_session_gnutls.cc +190 -0
- data/ext/ngtcp2/examples/tls_client_session_gnutls.h +52 -0
- data/ext/ngtcp2/examples/tls_client_session_openssl.cc +113 -0
- data/ext/ngtcp2/examples/tls_client_session_openssl.h +52 -0
- data/ext/ngtcp2/examples/tls_client_session_picotls.cc +147 -0
- data/ext/ngtcp2/examples/tls_client_session_picotls.h +52 -0
- data/ext/ngtcp2/examples/tls_client_session_wolfssl.cc +160 -0
- data/ext/ngtcp2/examples/tls_client_session_wolfssl.h +52 -0
- data/ext/ngtcp2/examples/tls_server_context.h +52 -0
- data/ext/ngtcp2/examples/tls_server_context_boringssl.cc +257 -0
- data/ext/ngtcp2/examples/tls_server_context_boringssl.h +54 -0
- data/ext/ngtcp2/examples/tls_server_context_gnutls.cc +99 -0
- data/ext/ngtcp2/examples/tls_server_context_gnutls.h +59 -0
- data/ext/ngtcp2/examples/tls_server_context_openssl.cc +338 -0
- data/ext/ngtcp2/examples/tls_server_context_openssl.h +54 -0
- data/ext/ngtcp2/examples/tls_server_context_picotls.cc +321 -0
- data/ext/ngtcp2/examples/tls_server_context_picotls.h +58 -0
- data/ext/ngtcp2/examples/tls_server_context_wolfssl.cc +284 -0
- data/ext/ngtcp2/examples/tls_server_context_wolfssl.h +55 -0
- data/ext/ngtcp2/examples/tls_server_session.h +52 -0
- data/ext/ngtcp2/examples/tls_server_session_boringssl.cc +84 -0
- data/ext/ngtcp2/examples/tls_server_session_boringssl.h +47 -0
- data/ext/ngtcp2/examples/tls_server_session_gnutls.cc +155 -0
- data/ext/ngtcp2/examples/tls_server_session_gnutls.h +46 -0
- data/ext/ngtcp2/examples/tls_server_session_openssl.cc +54 -0
- data/ext/ngtcp2/examples/tls_server_session_openssl.h +47 -0
- data/ext/ngtcp2/examples/tls_server_session_picotls.cc +70 -0
- data/ext/ngtcp2/examples/tls_server_session_picotls.h +47 -0
- data/ext/ngtcp2/examples/tls_server_session_wolfssl.cc +55 -0
- data/ext/ngtcp2/examples/tls_server_session_wolfssl.h +47 -0
- data/ext/ngtcp2/examples/tls_session_base_gnutls.cc +87 -0
- data/ext/ngtcp2/examples/tls_session_base_gnutls.h +51 -0
- data/ext/ngtcp2/examples/tls_session_base_openssl.cc +54 -0
- data/ext/ngtcp2/examples/tls_session_base_openssl.h +52 -0
- data/ext/ngtcp2/examples/tls_session_base_picotls.cc +56 -0
- data/ext/ngtcp2/examples/tls_session_base_picotls.h +54 -0
- data/ext/ngtcp2/examples/tls_session_base_wolfssl.cc +54 -0
- data/ext/ngtcp2/examples/tls_session_base_wolfssl.h +54 -0
- data/ext/ngtcp2/examples/tls_shared_picotls.cc +59 -0
- data/ext/ngtcp2/examples/tls_shared_picotls.h +36 -0
- data/ext/ngtcp2/examples/util.cc +646 -0
- data/ext/ngtcp2/examples/util.h +361 -0
- data/ext/ngtcp2/examples/util_gnutls.cc +136 -0
- data/ext/ngtcp2/examples/util_openssl.cc +131 -0
- data/ext/ngtcp2/examples/util_test.cc +237 -0
- data/ext/ngtcp2/examples/util_test.h +45 -0
- data/ext/ngtcp2/examples/util_wolfssl.cc +130 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/ack +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/ack_ecn +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/connection_close +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/crypto +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/data_blocked +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/datagram +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/datagram_len +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/max_data +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/max_stream_data +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/max_streams +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/new_connection_id +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/new_token +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/path_challenge +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/path_response +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/reset_stream +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/retire_connection_id +1 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/stop_sending +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/stream +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/stream_data_blocked +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/stream_len +0 -0
- data/ext/ngtcp2/fuzz/corpus/decode_frame/streams_blocked +0 -0
- data/ext/ngtcp2/fuzz/corpus/ksl/random +0 -0
- data/ext/ngtcp2/fuzz/decode_frame.cc +25 -0
- data/ext/ngtcp2/fuzz/ksl.cc +77 -0
- data/ext/ngtcp2/interop/Dockerfile +39 -0
- data/ext/ngtcp2/interop/run_endpoint.sh +93 -0
- data/ext/ngtcp2/lib/CMakeLists.txt +110 -0
- data/ext/ngtcp2/lib/Makefile.am +122 -0
- data/ext/ngtcp2/lib/includes/CMakeLists.txt +4 -0
- data/ext/ngtcp2/lib/includes/Makefile.am +25 -0
- data/ext/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +5843 -0
- data/ext/ngtcp2/lib/includes/ngtcp2/version.h.in +51 -0
- data/ext/ngtcp2/lib/libngtcp2.pc.in +33 -0
- data/ext/ngtcp2/lib/ngtcp2_acktr.c +335 -0
- data/ext/ngtcp2/lib/ngtcp2_acktr.h +221 -0
- data/ext/ngtcp2/lib/ngtcp2_addr.c +117 -0
- data/ext/ngtcp2/lib/ngtcp2_addr.h +69 -0
- data/ext/ngtcp2/lib/ngtcp2_balloc.c +90 -0
- data/ext/ngtcp2/lib/ngtcp2_balloc.h +91 -0
- data/ext/ngtcp2/lib/ngtcp2_bbr.c +693 -0
- data/ext/ngtcp2/lib/ngtcp2_bbr.h +157 -0
- data/ext/ngtcp2/lib/ngtcp2_bbr2.c +1490 -0
- data/ext/ngtcp2/lib/ngtcp2_bbr2.h +149 -0
- data/ext/ngtcp2/lib/ngtcp2_buf.c +56 -0
- data/ext/ngtcp2/lib/ngtcp2_buf.h +108 -0
- data/ext/ngtcp2/lib/ngtcp2_cc.c +616 -0
- data/ext/ngtcp2/lib/ngtcp2_cc.h +422 -0
- data/ext/ngtcp2/lib/ngtcp2_cid.c +147 -0
- data/ext/ngtcp2/lib/ngtcp2_cid.h +175 -0
- data/ext/ngtcp2/lib/ngtcp2_conn.c +13731 -0
- data/ext/ngtcp2/lib/ngtcp2_conn.h +1119 -0
- data/ext/ngtcp2/lib/ngtcp2_conn_stat.h +131 -0
- data/ext/ngtcp2/lib/ngtcp2_conv.c +291 -0
- data/ext/ngtcp2/lib/ngtcp2_conv.h +208 -0
- data/ext/ngtcp2/lib/ngtcp2_crypto.c +895 -0
- data/ext/ngtcp2/lib/ngtcp2_crypto.h +148 -0
- data/ext/ngtcp2/lib/ngtcp2_err.c +154 -0
- data/ext/ngtcp2/lib/ngtcp2_err.h +34 -0
- data/ext/ngtcp2/lib/ngtcp2_gaptr.c +167 -0
- data/ext/ngtcp2/lib/ngtcp2_gaptr.h +98 -0
- data/ext/ngtcp2/lib/ngtcp2_idtr.c +79 -0
- data/ext/ngtcp2/lib/ngtcp2_idtr.h +89 -0
- data/ext/ngtcp2/lib/ngtcp2_ksl.c +819 -0
- data/ext/ngtcp2/lib/ngtcp2_ksl.h +345 -0
- data/ext/ngtcp2/lib/ngtcp2_log.c +822 -0
- data/ext/ngtcp2/lib/ngtcp2_log.h +123 -0
- data/ext/ngtcp2/lib/ngtcp2_macro.h +58 -0
- data/ext/ngtcp2/lib/ngtcp2_map.c +336 -0
- data/ext/ngtcp2/lib/ngtcp2_map.h +136 -0
- data/ext/ngtcp2/lib/ngtcp2_mem.c +113 -0
- data/ext/ngtcp2/lib/ngtcp2_mem.h +72 -0
- data/ext/ngtcp2/lib/ngtcp2_net.h +136 -0
- data/ext/ngtcp2/lib/ngtcp2_objalloc.c +40 -0
- data/ext/ngtcp2/lib/ngtcp2_objalloc.h +140 -0
- data/ext/ngtcp2/lib/ngtcp2_opl.c +46 -0
- data/ext/ngtcp2/lib/ngtcp2_opl.h +65 -0
- data/ext/ngtcp2/lib/ngtcp2_path.c +77 -0
- data/ext/ngtcp2/lib/ngtcp2_path.h +49 -0
- data/ext/ngtcp2/lib/ngtcp2_pkt.c +2527 -0
- data/ext/ngtcp2/lib/ngtcp2_pkt.h +1235 -0
- data/ext/ngtcp2/lib/ngtcp2_pmtud.c +160 -0
- data/ext/ngtcp2/lib/ngtcp2_pmtud.h +123 -0
- data/ext/ngtcp2/lib/ngtcp2_ppe.c +230 -0
- data/ext/ngtcp2/lib/ngtcp2_ppe.h +153 -0
- data/ext/ngtcp2/lib/ngtcp2_pq.c +164 -0
- data/ext/ngtcp2/lib/ngtcp2_pq.h +126 -0
- data/ext/ngtcp2/lib/ngtcp2_pv.c +172 -0
- data/ext/ngtcp2/lib/ngtcp2_pv.h +194 -0
- data/ext/ngtcp2/lib/ngtcp2_qlog.c +1219 -0
- data/ext/ngtcp2/lib/ngtcp2_qlog.h +161 -0
- data/ext/ngtcp2/lib/ngtcp2_range.c +61 -0
- data/ext/ngtcp2/lib/ngtcp2_range.h +80 -0
- data/ext/ngtcp2/lib/ngtcp2_rcvry.h +40 -0
- data/ext/ngtcp2/lib/ngtcp2_ringbuf.c +121 -0
- data/ext/ngtcp2/lib/ngtcp2_ringbuf.h +132 -0
- data/ext/ngtcp2/lib/ngtcp2_rob.c +319 -0
- data/ext/ngtcp2/lib/ngtcp2_rob.h +197 -0
- data/ext/ngtcp2/lib/ngtcp2_rst.c +138 -0
- data/ext/ngtcp2/lib/ngtcp2_rst.h +86 -0
- data/ext/ngtcp2/lib/ngtcp2_rtb.c +1676 -0
- data/ext/ngtcp2/lib/ngtcp2_rtb.h +468 -0
- data/ext/ngtcp2/lib/ngtcp2_str.c +233 -0
- data/ext/ngtcp2/lib/ngtcp2_str.h +94 -0
- data/ext/ngtcp2/lib/ngtcp2_strm.c +698 -0
- data/ext/ngtcp2/lib/ngtcp2_strm.h +310 -0
- data/ext/ngtcp2/lib/ngtcp2_unreachable.c +71 -0
- data/ext/ngtcp2/lib/ngtcp2_unreachable.h +46 -0
- data/ext/ngtcp2/lib/ngtcp2_vec.c +243 -0
- data/ext/ngtcp2/lib/ngtcp2_vec.h +120 -0
- data/ext/ngtcp2/lib/ngtcp2_version.c +39 -0
- data/ext/ngtcp2/lib/ngtcp2_window_filter.c +99 -0
- data/ext/ngtcp2/lib/ngtcp2_window_filter.h +65 -0
- data/ext/ngtcp2/m4/ax_check_compile_flag.m4 +74 -0
- data/ext/ngtcp2/m4/ax_cxx_compile_stdcxx.m4 +1009 -0
- data/ext/ngtcp2/tests/CMakeLists.txt +68 -0
- data/ext/ngtcp2/tests/Makefile.am +94 -0
- data/ext/ngtcp2/tests/main.c +358 -0
- data/ext/ngtcp2/tests/ngtcp2_acktr_test.c +367 -0
- data/ext/ngtcp2/tests/ngtcp2_acktr_test.h +37 -0
- data/ext/ngtcp2/tests/ngtcp2_conn_test.c +9821 -0
- data/ext/ngtcp2/tests/ngtcp2_conn_test.h +104 -0
- data/ext/ngtcp2/tests/ngtcp2_conv_test.c +430 -0
- data/ext/ngtcp2/tests/ngtcp2_conv_test.h +46 -0
- data/ext/ngtcp2/tests/ngtcp2_crypto_test.c +667 -0
- data/ext/ngtcp2/tests/ngtcp2_crypto_test.h +35 -0
- data/ext/ngtcp2/tests/ngtcp2_gaptr_test.c +127 -0
- data/ext/ngtcp2/tests/ngtcp2_gaptr_test.h +36 -0
- data/ext/ngtcp2/tests/ngtcp2_idtr_test.c +79 -0
- data/ext/ngtcp2/tests/ngtcp2_idtr_test.h +34 -0
- data/ext/ngtcp2/tests/ngtcp2_ksl_test.c +502 -0
- data/ext/ngtcp2/tests/ngtcp2_ksl_test.h +39 -0
- data/ext/ngtcp2/tests/ngtcp2_map_test.c +206 -0
- data/ext/ngtcp2/tests/ngtcp2_map_test.h +38 -0
- data/ext/ngtcp2/tests/ngtcp2_pkt_test.c +1645 -0
- data/ext/ngtcp2/tests/ngtcp2_pkt_test.h +68 -0
- data/ext/ngtcp2/tests/ngtcp2_pmtud_test.c +153 -0
- data/ext/ngtcp2/tests/ngtcp2_pmtud_test.h +34 -0
- data/ext/ngtcp2/tests/ngtcp2_pv_test.c +129 -0
- data/ext/ngtcp2/tests/ngtcp2_pv_test.h +35 -0
- data/ext/ngtcp2/tests/ngtcp2_range_test.c +105 -0
- data/ext/ngtcp2/tests/ngtcp2_range_test.h +36 -0
- data/ext/ngtcp2/tests/ngtcp2_ringbuf_test.c +91 -0
- data/ext/ngtcp2/tests/ngtcp2_ringbuf_test.h +35 -0
- data/ext/ngtcp2/tests/ngtcp2_rob_test.c +552 -0
- data/ext/ngtcp2/tests/ngtcp2_rob_test.h +37 -0
- data/ext/ngtcp2/tests/ngtcp2_rtb_test.c +470 -0
- data/ext/ngtcp2/tests/ngtcp2_rtb_test.h +38 -0
- data/ext/ngtcp2/tests/ngtcp2_str_test.c +96 -0
- data/ext/ngtcp2/tests/ngtcp2_str_test.h +36 -0
- data/ext/ngtcp2/tests/ngtcp2_strm_test.c +575 -0
- data/ext/ngtcp2/tests/ngtcp2_strm_test.h +36 -0
- data/ext/ngtcp2/tests/ngtcp2_test_helper.c +404 -0
- data/ext/ngtcp2/tests/ngtcp2_test_helper.h +191 -0
- data/ext/ngtcp2/tests/ngtcp2_vec_test.c +426 -0
- data/ext/ngtcp2/tests/ngtcp2_vec_test.h +36 -0
- data/ext/ngtcp2/third-party/CMakeLists.txt +34 -0
- data/ext/ngtcp2/third-party/Makefile.am +31 -0
- data/ext/ngtcp2/third-party/http-parser/AUTHORS +68 -0
- data/ext/ngtcp2/third-party/http-parser/LICENSE-MIT +23 -0
- data/ext/ngtcp2/third-party/http-parser/Makefile +157 -0
- data/ext/ngtcp2/third-party/http-parser/README.md +246 -0
- data/ext/ngtcp2/third-party/http-parser/bench.c +111 -0
- data/ext/ngtcp2/third-party/http-parser/contrib/parsertrace.c +160 -0
- data/ext/ngtcp2/third-party/http-parser/contrib/url_parser.c +47 -0
- data/ext/ngtcp2/third-party/http-parser/http_parser.c +2419 -0
- data/ext/ngtcp2/third-party/http-parser/http_parser.gyp +111 -0
- data/ext/ngtcp2/third-party/http-parser/http_parser.h +431 -0
- data/ext/ngtcp2/third-party/http-parser/test.c +4411 -0
- data/lib/protocol/quic/version.rb +10 -0
- data/lib/protocol/quic.rb +9 -0
- data/license.md +21 -0
- data.tar.gz.sig +1 -0
- metadata +424 -0
- metadata.gz.sig +1 -0
|
@@ -0,0 +1,3731 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ngtcp2
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2017 ngtcp2 contributors
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
* a copy of this software and associated documentation files (the
|
|
8
|
+
* "Software"), to deal in the Software without restriction, including
|
|
9
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
* the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be
|
|
15
|
+
* included in all copies or substantial portions of the Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
*/
|
|
25
|
+
#include <chrono>
|
|
26
|
+
#include <cstdlib>
|
|
27
|
+
#include <cassert>
|
|
28
|
+
#include <cstring>
|
|
29
|
+
#include <iostream>
|
|
30
|
+
#include <algorithm>
|
|
31
|
+
#include <memory>
|
|
32
|
+
#include <fstream>
|
|
33
|
+
#include <iomanip>
|
|
34
|
+
|
|
35
|
+
#include <unistd.h>
|
|
36
|
+
#include <getopt.h>
|
|
37
|
+
#include <sys/types.h>
|
|
38
|
+
#include <sys/socket.h>
|
|
39
|
+
#include <netdb.h>
|
|
40
|
+
#include <sys/stat.h>
|
|
41
|
+
#include <fcntl.h>
|
|
42
|
+
#include <sys/mman.h>
|
|
43
|
+
#include <netinet/udp.h>
|
|
44
|
+
#include <net/if.h>
|
|
45
|
+
|
|
46
|
+
#include <http-parser/http_parser.h>
|
|
47
|
+
|
|
48
|
+
#include "server.h"
|
|
49
|
+
#include "network.h"
|
|
50
|
+
#include "debug.h"
|
|
51
|
+
#include "util.h"
|
|
52
|
+
#include "shared.h"
|
|
53
|
+
#include "http.h"
|
|
54
|
+
#include "template.h"
|
|
55
|
+
|
|
56
|
+
using namespace ngtcp2;
|
|
57
|
+
using namespace std::literals;
|
|
58
|
+
|
|
59
|
+
namespace {
|
|
60
|
+
constexpr size_t NGTCP2_SV_SCIDLEN = 18;
|
|
61
|
+
} // namespace
|
|
62
|
+
|
|
63
|
+
namespace {
|
|
64
|
+
constexpr size_t MAX_DYNBUFLEN = 10_m;
|
|
65
|
+
} // namespace
|
|
66
|
+
|
|
67
|
+
namespace {
|
|
68
|
+
constexpr size_t max_preferred_versionslen = 4;
|
|
69
|
+
} // namespace
|
|
70
|
+
|
|
71
|
+
namespace {
|
|
72
|
+
auto randgen = util::make_mt19937();
|
|
73
|
+
} // namespace
|
|
74
|
+
|
|
75
|
+
Config config{};
|
|
76
|
+
|
|
77
|
+
Stream::Stream(int64_t stream_id, Handler *handler)
|
|
78
|
+
: stream_id(stream_id),
|
|
79
|
+
handler(handler),
|
|
80
|
+
data(nullptr),
|
|
81
|
+
datalen(0),
|
|
82
|
+
dynresp(false),
|
|
83
|
+
dyndataleft(0),
|
|
84
|
+
dynbuflen(0) {}
|
|
85
|
+
|
|
86
|
+
namespace {
|
|
87
|
+
constexpr auto NGTCP2_SERVER = "nghttp3/ngtcp2 server"sv;
|
|
88
|
+
} // namespace
|
|
89
|
+
|
|
90
|
+
namespace {
|
|
91
|
+
std::string make_status_body(unsigned int status_code) {
|
|
92
|
+
auto status_string = util::format_uint(status_code);
|
|
93
|
+
auto reason_phrase = http::get_reason_phrase(status_code);
|
|
94
|
+
|
|
95
|
+
std::string body;
|
|
96
|
+
body = "<html><head><title>";
|
|
97
|
+
body += status_string;
|
|
98
|
+
body += ' ';
|
|
99
|
+
body += reason_phrase;
|
|
100
|
+
body += "</title></head><body><h1>";
|
|
101
|
+
body += status_string;
|
|
102
|
+
body += ' ';
|
|
103
|
+
body += reason_phrase;
|
|
104
|
+
body += "</h1><hr><address>";
|
|
105
|
+
body += NGTCP2_SERVER;
|
|
106
|
+
body += " at port ";
|
|
107
|
+
body += util::format_uint(config.port);
|
|
108
|
+
body += "</address>";
|
|
109
|
+
body += "</body></html>";
|
|
110
|
+
return body;
|
|
111
|
+
}
|
|
112
|
+
} // namespace
|
|
113
|
+
|
|
114
|
+
struct Request {
|
|
115
|
+
std::string path;
|
|
116
|
+
struct {
|
|
117
|
+
int32_t urgency;
|
|
118
|
+
int inc;
|
|
119
|
+
} pri;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
namespace {
|
|
123
|
+
Request request_path(const std::string_view &uri, bool is_connect) {
|
|
124
|
+
http_parser_url u;
|
|
125
|
+
Request req;
|
|
126
|
+
|
|
127
|
+
req.pri.urgency = -1;
|
|
128
|
+
req.pri.inc = -1;
|
|
129
|
+
|
|
130
|
+
http_parser_url_init(&u);
|
|
131
|
+
|
|
132
|
+
if (auto rv = http_parser_parse_url(uri.data(), uri.size(), is_connect, &u);
|
|
133
|
+
rv != 0) {
|
|
134
|
+
return req;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (u.field_set & (1 << UF_PATH)) {
|
|
138
|
+
req.path = std::string(uri.data() + u.field_data[UF_PATH].off,
|
|
139
|
+
u.field_data[UF_PATH].len);
|
|
140
|
+
if (req.path.find('%') != std::string::npos) {
|
|
141
|
+
req.path = util::percent_decode(std::begin(req.path), std::end(req.path));
|
|
142
|
+
}
|
|
143
|
+
if (!req.path.empty() && req.path.back() == '/') {
|
|
144
|
+
req.path += "index.html";
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
req.path = "/index.html";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
req.path = util::normalize_path(req.path);
|
|
151
|
+
if (req.path == "/") {
|
|
152
|
+
req.path = "/index.html";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (u.field_set & (1 << UF_QUERY)) {
|
|
156
|
+
static constexpr auto urgency_prefix = "u="sv;
|
|
157
|
+
static constexpr auto inc_prefix = "i="sv;
|
|
158
|
+
auto q = std::string(uri.data() + u.field_data[UF_QUERY].off,
|
|
159
|
+
u.field_data[UF_QUERY].len);
|
|
160
|
+
for (auto p = std::begin(q); p != std::end(q);) {
|
|
161
|
+
if (util::istarts_with(p, std::end(q), std::begin(urgency_prefix),
|
|
162
|
+
std::end(urgency_prefix))) {
|
|
163
|
+
auto urgency_start = p + urgency_prefix.size();
|
|
164
|
+
auto urgency_end = std::find(urgency_start, std::end(q), '&');
|
|
165
|
+
if (urgency_start + 1 == urgency_end && '0' <= *urgency_start &&
|
|
166
|
+
*urgency_start <= '7') {
|
|
167
|
+
req.pri.urgency = *urgency_start - '0';
|
|
168
|
+
}
|
|
169
|
+
if (urgency_end == std::end(q)) {
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
p = urgency_end + 1;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (util::istarts_with(p, std::end(q), std::begin(inc_prefix),
|
|
176
|
+
std::end(inc_prefix))) {
|
|
177
|
+
auto inc_start = p + inc_prefix.size();
|
|
178
|
+
auto inc_end = std::find(inc_start, std::end(q), '&');
|
|
179
|
+
if (inc_start + 1 == inc_end &&
|
|
180
|
+
(*inc_start == '0' || *inc_start == '1')) {
|
|
181
|
+
req.pri.inc = *inc_start - '0';
|
|
182
|
+
}
|
|
183
|
+
if (inc_end == std::end(q)) {
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
p = inc_end + 1;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
p = std::find(p, std::end(q), '&');
|
|
191
|
+
if (p == std::end(q)) {
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
++p;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return req;
|
|
198
|
+
}
|
|
199
|
+
} // namespace
|
|
200
|
+
|
|
201
|
+
enum FileEntryFlag {
|
|
202
|
+
FILE_ENTRY_TYPE_DIR = 0x1,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
struct FileEntry {
|
|
206
|
+
uint64_t len;
|
|
207
|
+
void *map;
|
|
208
|
+
int fd;
|
|
209
|
+
uint8_t flags;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
namespace {
|
|
213
|
+
std::unordered_map<std::string, FileEntry> file_cache;
|
|
214
|
+
} // namespace
|
|
215
|
+
|
|
216
|
+
std::pair<FileEntry, int> Stream::open_file(const std::string &path) {
|
|
217
|
+
auto it = file_cache.find(path);
|
|
218
|
+
if (it != std::end(file_cache)) {
|
|
219
|
+
return {(*it).second, 0};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
auto fd = open(path.c_str(), O_RDONLY);
|
|
223
|
+
if (fd == -1) {
|
|
224
|
+
return {{}, -1};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
struct stat st {};
|
|
228
|
+
if (fstat(fd, &st) != 0) {
|
|
229
|
+
close(fd);
|
|
230
|
+
return {{}, -1};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
FileEntry fe{};
|
|
234
|
+
if (st.st_mode & S_IFDIR) {
|
|
235
|
+
fe.flags |= FILE_ENTRY_TYPE_DIR;
|
|
236
|
+
fe.fd = -1;
|
|
237
|
+
close(fd);
|
|
238
|
+
} else {
|
|
239
|
+
fe.fd = fd;
|
|
240
|
+
fe.len = st.st_size;
|
|
241
|
+
fe.map = mmap(nullptr, fe.len, PROT_READ, MAP_SHARED, fd, 0);
|
|
242
|
+
if (fe.map == MAP_FAILED) {
|
|
243
|
+
std::cerr << "mmap: " << strerror(errno) << std::endl;
|
|
244
|
+
close(fd);
|
|
245
|
+
return {{}, -1};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
file_cache.emplace(path, fe);
|
|
250
|
+
|
|
251
|
+
return {std::move(fe), 0};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
void Stream::map_file(const FileEntry &fe) {
|
|
255
|
+
data = static_cast<uint8_t *>(fe.map);
|
|
256
|
+
datalen = fe.len;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
int64_t Stream::find_dyn_length(const std::string_view &path) {
|
|
260
|
+
assert(path[0] == '/');
|
|
261
|
+
|
|
262
|
+
if (path.size() == 1) {
|
|
263
|
+
return -1;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
uint64_t n = 0;
|
|
267
|
+
|
|
268
|
+
for (auto it = std::begin(path) + 1; it != std::end(path); ++it) {
|
|
269
|
+
if (*it < '0' || '9' < *it) {
|
|
270
|
+
return -1;
|
|
271
|
+
}
|
|
272
|
+
auto d = *it - '0';
|
|
273
|
+
if (n > (((1ull << 62) - 1) - d) / 10) {
|
|
274
|
+
return -1;
|
|
275
|
+
}
|
|
276
|
+
n = n * 10 + d;
|
|
277
|
+
if (n > config.max_dyn_length) {
|
|
278
|
+
return -1;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return static_cast<int64_t>(n);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
namespace {
|
|
286
|
+
nghttp3_ssize read_data(nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec,
|
|
287
|
+
size_t veccnt, uint32_t *pflags, void *user_data,
|
|
288
|
+
void *stream_user_data) {
|
|
289
|
+
auto stream = static_cast<Stream *>(stream_user_data);
|
|
290
|
+
|
|
291
|
+
vec[0].base = stream->data;
|
|
292
|
+
vec[0].len = stream->datalen;
|
|
293
|
+
*pflags |= NGHTTP3_DATA_FLAG_EOF;
|
|
294
|
+
if (config.send_trailers) {
|
|
295
|
+
*pflags |= NGHTTP3_DATA_FLAG_NO_END_STREAM;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return 1;
|
|
299
|
+
}
|
|
300
|
+
} // namespace
|
|
301
|
+
|
|
302
|
+
auto dyn_buf = std::make_unique<std::array<uint8_t, 16_k>>();
|
|
303
|
+
|
|
304
|
+
namespace {
|
|
305
|
+
nghttp3_ssize dyn_read_data(nghttp3_conn *conn, int64_t stream_id,
|
|
306
|
+
nghttp3_vec *vec, size_t veccnt, uint32_t *pflags,
|
|
307
|
+
void *user_data, void *stream_user_data) {
|
|
308
|
+
auto stream = static_cast<Stream *>(stream_user_data);
|
|
309
|
+
|
|
310
|
+
if (stream->dynbuflen > MAX_DYNBUFLEN) {
|
|
311
|
+
return NGHTTP3_ERR_WOULDBLOCK;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
auto len =
|
|
315
|
+
std::min(dyn_buf->size(), static_cast<size_t>(stream->dyndataleft));
|
|
316
|
+
|
|
317
|
+
vec[0].base = dyn_buf->data();
|
|
318
|
+
vec[0].len = len;
|
|
319
|
+
|
|
320
|
+
stream->dynbuflen += len;
|
|
321
|
+
stream->dyndataleft -= len;
|
|
322
|
+
|
|
323
|
+
if (stream->dyndataleft == 0) {
|
|
324
|
+
*pflags |= NGHTTP3_DATA_FLAG_EOF;
|
|
325
|
+
if (config.send_trailers) {
|
|
326
|
+
*pflags |= NGHTTP3_DATA_FLAG_NO_END_STREAM;
|
|
327
|
+
auto stream_id_str = util::format_uint(stream_id);
|
|
328
|
+
std::array<nghttp3_nv, 1> trailers{
|
|
329
|
+
util::make_nv_nc("x-ngtcp2-stream-id"sv, stream_id_str),
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
if (auto rv = nghttp3_conn_submit_trailers(
|
|
333
|
+
conn, stream_id, trailers.data(), trailers.size());
|
|
334
|
+
rv != 0) {
|
|
335
|
+
std::cerr << "nghttp3_conn_submit_trailers: " << nghttp3_strerror(rv)
|
|
336
|
+
<< std::endl;
|
|
337
|
+
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return 1;
|
|
343
|
+
}
|
|
344
|
+
} // namespace
|
|
345
|
+
|
|
346
|
+
void Stream::http_acked_stream_data(uint64_t datalen) {
|
|
347
|
+
if (!dynresp) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
assert(dynbuflen >= datalen);
|
|
352
|
+
|
|
353
|
+
dynbuflen -= datalen;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
int Stream::send_status_response(nghttp3_conn *httpconn,
|
|
357
|
+
unsigned int status_code,
|
|
358
|
+
const std::vector<HTTPHeader> &extra_headers) {
|
|
359
|
+
status_resp_body = make_status_body(status_code);
|
|
360
|
+
|
|
361
|
+
auto status_code_str = util::format_uint(status_code);
|
|
362
|
+
auto content_length_str = util::format_uint(status_resp_body.size());
|
|
363
|
+
|
|
364
|
+
std::vector<nghttp3_nv> nva(4 + extra_headers.size());
|
|
365
|
+
nva[0] = util::make_nv_nc(":status"sv, status_code_str);
|
|
366
|
+
nva[1] = util::make_nv_nn("server"sv, NGTCP2_SERVER);
|
|
367
|
+
nva[2] = util::make_nv_nn("content-type"sv, "text/html; charset=utf-8");
|
|
368
|
+
nva[3] = util::make_nv_nc("content-length"sv, content_length_str);
|
|
369
|
+
for (size_t i = 0; i < extra_headers.size(); ++i) {
|
|
370
|
+
auto &hdr = extra_headers[i];
|
|
371
|
+
auto &nv = nva[4 + i];
|
|
372
|
+
nv = util::make_nv_cc(hdr.name, hdr.value);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
data = (uint8_t *)status_resp_body.data();
|
|
376
|
+
datalen = status_resp_body.size();
|
|
377
|
+
|
|
378
|
+
nghttp3_data_reader dr{};
|
|
379
|
+
dr.read_data = read_data;
|
|
380
|
+
|
|
381
|
+
if (auto rv = nghttp3_conn_submit_response(httpconn, stream_id, nva.data(),
|
|
382
|
+
nva.size(), &dr);
|
|
383
|
+
rv != 0) {
|
|
384
|
+
std::cerr << "nghttp3_conn_submit_response: " << nghttp3_strerror(rv)
|
|
385
|
+
<< std::endl;
|
|
386
|
+
return -1;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (config.send_trailers) {
|
|
390
|
+
auto stream_id_str = util::format_uint(stream_id);
|
|
391
|
+
std::array<nghttp3_nv, 1> trailers{
|
|
392
|
+
util::make_nv_nc("x-ngtcp2-stream-id"sv, stream_id_str),
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
if (auto rv = nghttp3_conn_submit_trailers(
|
|
396
|
+
httpconn, stream_id, trailers.data(), trailers.size());
|
|
397
|
+
rv != 0) {
|
|
398
|
+
std::cerr << "nghttp3_conn_submit_trailers: " << nghttp3_strerror(rv)
|
|
399
|
+
<< std::endl;
|
|
400
|
+
return -1;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
handler->shutdown_read(stream_id, NGHTTP3_H3_NO_ERROR);
|
|
405
|
+
|
|
406
|
+
return 0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
int Stream::send_redirect_response(nghttp3_conn *httpconn,
|
|
410
|
+
unsigned int status_code,
|
|
411
|
+
const std::string_view &path) {
|
|
412
|
+
return send_status_response(httpconn, status_code, {{"location", path}});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
int Stream::start_response(nghttp3_conn *httpconn) {
|
|
416
|
+
// TODO This should be handled by nghttp3
|
|
417
|
+
if (uri.empty() || method.empty()) {
|
|
418
|
+
return send_status_response(httpconn, 400);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
auto req = request_path(uri, method == "CONNECT");
|
|
422
|
+
if (req.path.empty()) {
|
|
423
|
+
return send_status_response(httpconn, 400);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
auto dyn_len = find_dyn_length(req.path);
|
|
427
|
+
|
|
428
|
+
int64_t content_length = -1;
|
|
429
|
+
nghttp3_data_reader dr{};
|
|
430
|
+
auto content_type = "text/plain"sv;
|
|
431
|
+
|
|
432
|
+
if (dyn_len == -1) {
|
|
433
|
+
auto path = config.htdocs + req.path;
|
|
434
|
+
auto [fe, rv] = open_file(path);
|
|
435
|
+
if (rv != 0) {
|
|
436
|
+
send_status_response(httpconn, 404);
|
|
437
|
+
return 0;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (fe.flags & FILE_ENTRY_TYPE_DIR) {
|
|
441
|
+
send_redirect_response(httpconn, 308,
|
|
442
|
+
path.substr(config.htdocs.size() - 1) + '/');
|
|
443
|
+
return 0;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
content_length = fe.len;
|
|
447
|
+
|
|
448
|
+
if (method != "HEAD") {
|
|
449
|
+
map_file(fe);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
dr.read_data = read_data;
|
|
453
|
+
|
|
454
|
+
auto ext = std::end(req.path) - 1;
|
|
455
|
+
for (; ext != std::begin(req.path) && *ext != '.' && *ext != '/'; --ext)
|
|
456
|
+
;
|
|
457
|
+
if (*ext == '.') {
|
|
458
|
+
++ext;
|
|
459
|
+
auto it = config.mime_types.find(std::string{ext, std::end(req.path)});
|
|
460
|
+
if (it != std::end(config.mime_types)) {
|
|
461
|
+
content_type = (*it).second;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
} else {
|
|
465
|
+
content_length = dyn_len;
|
|
466
|
+
dynresp = true;
|
|
467
|
+
dr.read_data = dyn_read_data;
|
|
468
|
+
|
|
469
|
+
if (method != "HEAD") {
|
|
470
|
+
datalen = dyn_len;
|
|
471
|
+
dyndataleft = dyn_len;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
content_type = "application/octet-stream"sv;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
auto content_length_str = util::format_uint(content_length);
|
|
478
|
+
|
|
479
|
+
std::array<nghttp3_nv, 5> nva{
|
|
480
|
+
util::make_nv_nn(":status"sv, "200"sv),
|
|
481
|
+
util::make_nv_nn("server"sv, NGTCP2_SERVER),
|
|
482
|
+
util::make_nv_nn("content-type"sv, content_type),
|
|
483
|
+
util::make_nv_nc("content-length"sv, content_length_str),
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
size_t nvlen = 4;
|
|
487
|
+
|
|
488
|
+
std::string prival;
|
|
489
|
+
|
|
490
|
+
if (req.pri.urgency != -1 || req.pri.inc != -1) {
|
|
491
|
+
nghttp3_pri pri;
|
|
492
|
+
|
|
493
|
+
if (auto rv = nghttp3_conn_get_stream_priority(httpconn, &pri, stream_id);
|
|
494
|
+
rv != 0) {
|
|
495
|
+
std::cerr << "nghttp3_conn_get_stream_priority: " << nghttp3_strerror(rv)
|
|
496
|
+
<< std::endl;
|
|
497
|
+
return -1;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (req.pri.urgency != -1) {
|
|
501
|
+
pri.urgency = req.pri.urgency;
|
|
502
|
+
}
|
|
503
|
+
if (req.pri.inc != -1) {
|
|
504
|
+
pri.inc = req.pri.inc;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (auto rv = nghttp3_conn_set_stream_priority(httpconn, stream_id, &pri);
|
|
508
|
+
rv != 0) {
|
|
509
|
+
std::cerr << "nghttp3_conn_set_stream_priority: " << nghttp3_strerror(rv)
|
|
510
|
+
<< std::endl;
|
|
511
|
+
return -1;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
prival = "u=";
|
|
515
|
+
prival += pri.urgency + '0';
|
|
516
|
+
prival += ",i";
|
|
517
|
+
if (!pri.inc) {
|
|
518
|
+
prival += "=?0";
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
nva[nvlen++] = util::make_nv_nc("priority"sv, prival);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (!config.quiet) {
|
|
525
|
+
debug::print_http_response_headers(stream_id, nva.data(), nvlen);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (auto rv = nghttp3_conn_submit_response(httpconn, stream_id, nva.data(),
|
|
529
|
+
nvlen, &dr);
|
|
530
|
+
rv != 0) {
|
|
531
|
+
std::cerr << "nghttp3_conn_submit_response: " << nghttp3_strerror(rv)
|
|
532
|
+
<< std::endl;
|
|
533
|
+
return -1;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (config.send_trailers && dyn_len == -1) {
|
|
537
|
+
auto stream_id_str = util::format_uint(stream_id);
|
|
538
|
+
std::array<nghttp3_nv, 1> trailers{
|
|
539
|
+
util::make_nv_nc("x-ngtcp2-stream-id"sv, stream_id_str),
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
if (auto rv = nghttp3_conn_submit_trailers(
|
|
543
|
+
httpconn, stream_id, trailers.data(), trailers.size());
|
|
544
|
+
rv != 0) {
|
|
545
|
+
std::cerr << "nghttp3_conn_submit_trailers: " << nghttp3_strerror(rv)
|
|
546
|
+
<< std::endl;
|
|
547
|
+
return -1;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return 0;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
namespace {
|
|
555
|
+
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
|
556
|
+
auto h = static_cast<Handler *>(w->data);
|
|
557
|
+
auto s = h->server();
|
|
558
|
+
|
|
559
|
+
switch (h->on_write()) {
|
|
560
|
+
case 0:
|
|
561
|
+
case NETWORK_ERR_CLOSE_WAIT:
|
|
562
|
+
return;
|
|
563
|
+
default:
|
|
564
|
+
s->remove(h);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
} // namespace
|
|
568
|
+
|
|
569
|
+
namespace {
|
|
570
|
+
void close_waitcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
|
571
|
+
auto h = static_cast<Handler *>(w->data);
|
|
572
|
+
auto s = h->server();
|
|
573
|
+
auto conn = h->conn();
|
|
574
|
+
|
|
575
|
+
if (ngtcp2_conn_is_in_closing_period(conn)) {
|
|
576
|
+
if (!config.quiet) {
|
|
577
|
+
std::cerr << "Closing Period is over" << std::endl;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
s->remove(h);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (ngtcp2_conn_is_in_draining_period(conn)) {
|
|
584
|
+
if (!config.quiet) {
|
|
585
|
+
std::cerr << "Draining Period is over" << std::endl;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
s->remove(h);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
assert(0);
|
|
593
|
+
}
|
|
594
|
+
} // namespace
|
|
595
|
+
|
|
596
|
+
namespace {
|
|
597
|
+
void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
|
598
|
+
int rv;
|
|
599
|
+
|
|
600
|
+
auto h = static_cast<Handler *>(w->data);
|
|
601
|
+
auto s = h->server();
|
|
602
|
+
|
|
603
|
+
if (!config.quiet) {
|
|
604
|
+
std::cerr << "Timer expired" << std::endl;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
rv = h->handle_expiry();
|
|
608
|
+
if (rv != 0) {
|
|
609
|
+
goto fail;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
rv = h->on_write();
|
|
613
|
+
if (rv != 0) {
|
|
614
|
+
goto fail;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return;
|
|
618
|
+
|
|
619
|
+
fail:
|
|
620
|
+
switch (rv) {
|
|
621
|
+
case NETWORK_ERR_CLOSE_WAIT:
|
|
622
|
+
ev_timer_stop(loop, w);
|
|
623
|
+
return;
|
|
624
|
+
default:
|
|
625
|
+
s->remove(h);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
} // namespace
|
|
630
|
+
|
|
631
|
+
Handler::Handler(struct ev_loop *loop, Server *server)
|
|
632
|
+
: loop_(loop),
|
|
633
|
+
server_(server),
|
|
634
|
+
qlog_(nullptr),
|
|
635
|
+
scid_{},
|
|
636
|
+
httpconn_{nullptr},
|
|
637
|
+
nkey_update_(0),
|
|
638
|
+
no_gso_{
|
|
639
|
+
#ifdef UDP_SEGMENT
|
|
640
|
+
false
|
|
641
|
+
#else // !UDP_SEGMENT
|
|
642
|
+
true
|
|
643
|
+
#endif // !UDP_SEGMENT
|
|
644
|
+
},
|
|
645
|
+
tx_{
|
|
646
|
+
.data = std::unique_ptr<uint8_t[]>(new uint8_t[64_k]),
|
|
647
|
+
} {
|
|
648
|
+
ev_io_init(&wev_, writecb, 0, EV_WRITE);
|
|
649
|
+
wev_.data = this;
|
|
650
|
+
ev_timer_init(&timer_, timeoutcb, 0., 0.);
|
|
651
|
+
timer_.data = this;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
Handler::~Handler() {
|
|
655
|
+
if (!config.quiet) {
|
|
656
|
+
std::cerr << scid_ << " Closing QUIC connection " << std::endl;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
ev_timer_stop(loop_, &timer_);
|
|
660
|
+
ev_io_stop(loop_, &wev_);
|
|
661
|
+
|
|
662
|
+
if (httpconn_) {
|
|
663
|
+
nghttp3_conn_del(httpconn_);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (qlog_) {
|
|
667
|
+
fclose(qlog_);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
namespace {
|
|
672
|
+
int handshake_completed(ngtcp2_conn *conn, void *user_data) {
|
|
673
|
+
auto h = static_cast<Handler *>(user_data);
|
|
674
|
+
|
|
675
|
+
if (!config.quiet) {
|
|
676
|
+
debug::handshake_completed(conn, user_data);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (h->handshake_completed() != 0) {
|
|
680
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return 0;
|
|
684
|
+
}
|
|
685
|
+
} // namespace
|
|
686
|
+
|
|
687
|
+
int Handler::handshake_completed() {
|
|
688
|
+
if (!config.quiet) {
|
|
689
|
+
std::cerr << "Negotiated cipher suite is " << tls_session_.get_cipher_name()
|
|
690
|
+
<< std::endl;
|
|
691
|
+
std::cerr << "Negotiated ALPN is " << tls_session_.get_selected_alpn()
|
|
692
|
+
<< std::endl;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (tls_session_.send_session_ticket() != 0) {
|
|
696
|
+
std::cerr << "Unable to send session ticket" << std::endl;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
std::array<uint8_t, NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN> token;
|
|
700
|
+
|
|
701
|
+
auto path = ngtcp2_conn_get_path(conn_);
|
|
702
|
+
auto t = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
703
|
+
std::chrono::system_clock::now().time_since_epoch())
|
|
704
|
+
.count();
|
|
705
|
+
|
|
706
|
+
auto tokenlen = ngtcp2_crypto_generate_regular_token(
|
|
707
|
+
token.data(), config.static_secret.data(), config.static_secret.size(),
|
|
708
|
+
path->remote.addr, path->remote.addrlen, t);
|
|
709
|
+
if (tokenlen < 0) {
|
|
710
|
+
if (!config.quiet) {
|
|
711
|
+
std::cerr << "Unable to generate token" << std::endl;
|
|
712
|
+
}
|
|
713
|
+
return 0;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (auto rv = ngtcp2_conn_submit_new_token(conn_, token.data(), tokenlen);
|
|
717
|
+
rv != 0) {
|
|
718
|
+
if (!config.quiet) {
|
|
719
|
+
std::cerr << "ngtcp2_conn_submit_new_token: " << ngtcp2_strerror(rv)
|
|
720
|
+
<< std::endl;
|
|
721
|
+
}
|
|
722
|
+
return -1;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return 0;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
namespace {
|
|
729
|
+
int do_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
|
|
730
|
+
const ngtcp2_crypto_cipher_ctx *hp_ctx, const uint8_t *sample) {
|
|
731
|
+
if (ngtcp2_crypto_hp_mask(dest, hp, hp_ctx, sample) != 0) {
|
|
732
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (!config.quiet && config.show_secret) {
|
|
736
|
+
debug::print_hp_mask(dest, NGTCP2_HP_MASKLEN, sample, NGTCP2_HP_SAMPLELEN);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return 0;
|
|
740
|
+
}
|
|
741
|
+
} // namespace
|
|
742
|
+
|
|
743
|
+
namespace {
|
|
744
|
+
int recv_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
|
|
745
|
+
uint64_t offset, const uint8_t *data, size_t datalen,
|
|
746
|
+
void *user_data) {
|
|
747
|
+
if (!config.quiet && !config.no_quic_dump) {
|
|
748
|
+
debug::print_crypto_data(crypto_level, data, datalen);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return ngtcp2_crypto_recv_crypto_data_cb(conn, crypto_level, offset, data,
|
|
752
|
+
datalen, user_data);
|
|
753
|
+
}
|
|
754
|
+
} // namespace
|
|
755
|
+
|
|
756
|
+
namespace {
|
|
757
|
+
int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
|
|
758
|
+
uint64_t offset, const uint8_t *data, size_t datalen,
|
|
759
|
+
void *user_data, void *stream_user_data) {
|
|
760
|
+
auto h = static_cast<Handler *>(user_data);
|
|
761
|
+
|
|
762
|
+
if (h->recv_stream_data(flags, stream_id, data, datalen) != 0) {
|
|
763
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return 0;
|
|
767
|
+
}
|
|
768
|
+
} // namespace
|
|
769
|
+
|
|
770
|
+
namespace {
|
|
771
|
+
int acked_stream_data_offset(ngtcp2_conn *conn, int64_t stream_id,
|
|
772
|
+
uint64_t offset, uint64_t datalen, void *user_data,
|
|
773
|
+
void *stream_user_data) {
|
|
774
|
+
auto h = static_cast<Handler *>(user_data);
|
|
775
|
+
if (h->acked_stream_data_offset(stream_id, datalen) != 0) {
|
|
776
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
777
|
+
}
|
|
778
|
+
return 0;
|
|
779
|
+
}
|
|
780
|
+
} // namespace
|
|
781
|
+
|
|
782
|
+
int Handler::acked_stream_data_offset(int64_t stream_id, uint64_t datalen) {
|
|
783
|
+
if (!httpconn_) {
|
|
784
|
+
return 0;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (auto rv = nghttp3_conn_add_ack_offset(httpconn_, stream_id, datalen);
|
|
788
|
+
rv != 0) {
|
|
789
|
+
std::cerr << "nghttp3_conn_add_ack_offset: " << nghttp3_strerror(rv)
|
|
790
|
+
<< std::endl;
|
|
791
|
+
return -1;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return 0;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
namespace {
|
|
798
|
+
int stream_open(ngtcp2_conn *conn, int64_t stream_id, void *user_data) {
|
|
799
|
+
auto h = static_cast<Handler *>(user_data);
|
|
800
|
+
h->on_stream_open(stream_id);
|
|
801
|
+
return 0;
|
|
802
|
+
}
|
|
803
|
+
} // namespace
|
|
804
|
+
|
|
805
|
+
void Handler::on_stream_open(int64_t stream_id) {
|
|
806
|
+
if (!ngtcp2_is_bidi_stream(stream_id)) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
auto it = streams_.find(stream_id);
|
|
810
|
+
(void)it;
|
|
811
|
+
assert(it == std::end(streams_));
|
|
812
|
+
streams_.emplace(stream_id, std::make_unique<Stream>(stream_id, this));
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
namespace {
|
|
816
|
+
int stream_close(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
|
|
817
|
+
uint64_t app_error_code, void *user_data,
|
|
818
|
+
void *stream_user_data) {
|
|
819
|
+
auto h = static_cast<Handler *>(user_data);
|
|
820
|
+
|
|
821
|
+
if (!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
|
|
822
|
+
app_error_code = NGHTTP3_H3_NO_ERROR;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
if (h->on_stream_close(stream_id, app_error_code) != 0) {
|
|
826
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
827
|
+
}
|
|
828
|
+
return 0;
|
|
829
|
+
}
|
|
830
|
+
} // namespace
|
|
831
|
+
|
|
832
|
+
namespace {
|
|
833
|
+
int stream_reset(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size,
|
|
834
|
+
uint64_t app_error_code, void *user_data,
|
|
835
|
+
void *stream_user_data) {
|
|
836
|
+
auto h = static_cast<Handler *>(user_data);
|
|
837
|
+
if (h->on_stream_reset(stream_id) != 0) {
|
|
838
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
839
|
+
}
|
|
840
|
+
return 0;
|
|
841
|
+
}
|
|
842
|
+
} // namespace
|
|
843
|
+
|
|
844
|
+
int Handler::on_stream_reset(int64_t stream_id) {
|
|
845
|
+
if (httpconn_) {
|
|
846
|
+
if (auto rv = nghttp3_conn_shutdown_stream_read(httpconn_, stream_id);
|
|
847
|
+
rv != 0) {
|
|
848
|
+
std::cerr << "nghttp3_conn_shutdown_stream_read: " << nghttp3_strerror(rv)
|
|
849
|
+
<< std::endl;
|
|
850
|
+
return -1;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return 0;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
namespace {
|
|
857
|
+
int stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id,
|
|
858
|
+
uint64_t app_error_code, void *user_data,
|
|
859
|
+
void *stream_user_data) {
|
|
860
|
+
auto h = static_cast<Handler *>(user_data);
|
|
861
|
+
if (h->on_stream_stop_sending(stream_id) != 0) {
|
|
862
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
863
|
+
}
|
|
864
|
+
return 0;
|
|
865
|
+
}
|
|
866
|
+
} // namespace
|
|
867
|
+
|
|
868
|
+
int Handler::on_stream_stop_sending(int64_t stream_id) {
|
|
869
|
+
if (!httpconn_) {
|
|
870
|
+
return 0;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (auto rv = nghttp3_conn_shutdown_stream_read(httpconn_, stream_id);
|
|
874
|
+
rv != 0) {
|
|
875
|
+
std::cerr << "nghttp3_conn_shutdown_stream_read: " << nghttp3_strerror(rv)
|
|
876
|
+
<< std::endl;
|
|
877
|
+
return -1;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
return 0;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
namespace {
|
|
884
|
+
void rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx) {
|
|
885
|
+
auto dis = std::uniform_int_distribution<uint8_t>();
|
|
886
|
+
std::generate(dest, dest + destlen, [&dis]() { return dis(randgen); });
|
|
887
|
+
}
|
|
888
|
+
} // namespace
|
|
889
|
+
|
|
890
|
+
namespace {
|
|
891
|
+
int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, uint8_t *token,
|
|
892
|
+
size_t cidlen, void *user_data) {
|
|
893
|
+
if (util::generate_secure_random(cid->data, cidlen) != 0) {
|
|
894
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
cid->datalen = cidlen;
|
|
898
|
+
if (ngtcp2_crypto_generate_stateless_reset_token(
|
|
899
|
+
token, config.static_secret.data(), config.static_secret.size(),
|
|
900
|
+
cid) != 0) {
|
|
901
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
auto h = static_cast<Handler *>(user_data);
|
|
905
|
+
h->server()->associate_cid(cid, h);
|
|
906
|
+
|
|
907
|
+
return 0;
|
|
908
|
+
}
|
|
909
|
+
} // namespace
|
|
910
|
+
|
|
911
|
+
namespace {
|
|
912
|
+
int remove_connection_id(ngtcp2_conn *conn, const ngtcp2_cid *cid,
|
|
913
|
+
void *user_data) {
|
|
914
|
+
auto h = static_cast<Handler *>(user_data);
|
|
915
|
+
h->server()->dissociate_cid(cid);
|
|
916
|
+
return 0;
|
|
917
|
+
}
|
|
918
|
+
} // namespace
|
|
919
|
+
|
|
920
|
+
namespace {
|
|
921
|
+
int update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
|
|
922
|
+
ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
|
|
923
|
+
ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
|
|
924
|
+
const uint8_t *current_rx_secret,
|
|
925
|
+
const uint8_t *current_tx_secret, size_t secretlen,
|
|
926
|
+
void *user_data) {
|
|
927
|
+
auto h = static_cast<Handler *>(user_data);
|
|
928
|
+
if (h->update_key(rx_secret, tx_secret, rx_aead_ctx, rx_iv, tx_aead_ctx,
|
|
929
|
+
tx_iv, current_rx_secret, current_tx_secret,
|
|
930
|
+
secretlen) != 0) {
|
|
931
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
932
|
+
}
|
|
933
|
+
return 0;
|
|
934
|
+
}
|
|
935
|
+
} // namespace
|
|
936
|
+
|
|
937
|
+
namespace {
|
|
938
|
+
int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path,
|
|
939
|
+
ngtcp2_path_validation_result res, void *user_data) {
|
|
940
|
+
if (!config.quiet) {
|
|
941
|
+
debug::path_validation(path, res);
|
|
942
|
+
}
|
|
943
|
+
return 0;
|
|
944
|
+
}
|
|
945
|
+
} // namespace
|
|
946
|
+
|
|
947
|
+
namespace {
|
|
948
|
+
int extend_max_remote_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams,
|
|
949
|
+
void *user_data) {
|
|
950
|
+
auto h = static_cast<Handler *>(user_data);
|
|
951
|
+
h->extend_max_remote_streams_bidi(max_streams);
|
|
952
|
+
return 0;
|
|
953
|
+
}
|
|
954
|
+
} // namespace
|
|
955
|
+
|
|
956
|
+
void Handler::extend_max_remote_streams_bidi(uint64_t max_streams) {
|
|
957
|
+
if (!httpconn_) {
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
nghttp3_conn_set_max_client_streams_bidi(httpconn_, max_streams);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
namespace {
|
|
965
|
+
int http_recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data,
|
|
966
|
+
size_t datalen, void *user_data, void *stream_user_data) {
|
|
967
|
+
if (!config.quiet && !config.no_http_dump) {
|
|
968
|
+
debug::print_http_data(stream_id, data, datalen);
|
|
969
|
+
}
|
|
970
|
+
auto h = static_cast<Handler *>(user_data);
|
|
971
|
+
h->http_consume(stream_id, datalen);
|
|
972
|
+
return 0;
|
|
973
|
+
}
|
|
974
|
+
} // namespace
|
|
975
|
+
|
|
976
|
+
namespace {
|
|
977
|
+
int http_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
|
|
978
|
+
size_t nconsumed, void *user_data,
|
|
979
|
+
void *stream_user_data) {
|
|
980
|
+
auto h = static_cast<Handler *>(user_data);
|
|
981
|
+
h->http_consume(stream_id, nconsumed);
|
|
982
|
+
return 0;
|
|
983
|
+
}
|
|
984
|
+
} // namespace
|
|
985
|
+
|
|
986
|
+
void Handler::http_consume(int64_t stream_id, size_t nconsumed) {
|
|
987
|
+
ngtcp2_conn_extend_max_stream_offset(conn_, stream_id, nconsumed);
|
|
988
|
+
ngtcp2_conn_extend_max_offset(conn_, nconsumed);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
namespace {
|
|
992
|
+
int http_begin_request_headers(nghttp3_conn *conn, int64_t stream_id,
|
|
993
|
+
void *user_data, void *stream_user_data) {
|
|
994
|
+
if (!config.quiet) {
|
|
995
|
+
debug::print_http_begin_request_headers(stream_id);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
auto h = static_cast<Handler *>(user_data);
|
|
999
|
+
h->http_begin_request_headers(stream_id);
|
|
1000
|
+
return 0;
|
|
1001
|
+
}
|
|
1002
|
+
} // namespace
|
|
1003
|
+
|
|
1004
|
+
void Handler::http_begin_request_headers(int64_t stream_id) {
|
|
1005
|
+
auto it = streams_.find(stream_id);
|
|
1006
|
+
assert(it != std::end(streams_));
|
|
1007
|
+
auto &stream = (*it).second;
|
|
1008
|
+
|
|
1009
|
+
nghttp3_conn_set_stream_user_data(httpconn_, stream_id, stream.get());
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
namespace {
|
|
1013
|
+
int http_recv_request_header(nghttp3_conn *conn, int64_t stream_id,
|
|
1014
|
+
int32_t token, nghttp3_rcbuf *name,
|
|
1015
|
+
nghttp3_rcbuf *value, uint8_t flags,
|
|
1016
|
+
void *user_data, void *stream_user_data) {
|
|
1017
|
+
if (!config.quiet) {
|
|
1018
|
+
debug::print_http_header(stream_id, name, value, flags);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
auto h = static_cast<Handler *>(user_data);
|
|
1022
|
+
auto stream = static_cast<Stream *>(stream_user_data);
|
|
1023
|
+
h->http_recv_request_header(stream, token, name, value);
|
|
1024
|
+
return 0;
|
|
1025
|
+
}
|
|
1026
|
+
} // namespace
|
|
1027
|
+
|
|
1028
|
+
void Handler::http_recv_request_header(Stream *stream, int32_t token,
|
|
1029
|
+
nghttp3_rcbuf *name,
|
|
1030
|
+
nghttp3_rcbuf *value) {
|
|
1031
|
+
auto v = nghttp3_rcbuf_get_buf(value);
|
|
1032
|
+
|
|
1033
|
+
switch (token) {
|
|
1034
|
+
case NGHTTP3_QPACK_TOKEN__PATH:
|
|
1035
|
+
stream->uri = std::string{v.base, v.base + v.len};
|
|
1036
|
+
break;
|
|
1037
|
+
case NGHTTP3_QPACK_TOKEN__METHOD:
|
|
1038
|
+
stream->method = std::string{v.base, v.base + v.len};
|
|
1039
|
+
break;
|
|
1040
|
+
case NGHTTP3_QPACK_TOKEN__AUTHORITY:
|
|
1041
|
+
stream->authority = std::string{v.base, v.base + v.len};
|
|
1042
|
+
break;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
namespace {
|
|
1047
|
+
int http_end_request_headers(nghttp3_conn *conn, int64_t stream_id, int fin,
|
|
1048
|
+
void *user_data, void *stream_user_data) {
|
|
1049
|
+
if (!config.quiet) {
|
|
1050
|
+
debug::print_http_end_headers(stream_id);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
auto h = static_cast<Handler *>(user_data);
|
|
1054
|
+
auto stream = static_cast<Stream *>(stream_user_data);
|
|
1055
|
+
if (h->http_end_request_headers(stream) != 0) {
|
|
1056
|
+
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
|
1057
|
+
}
|
|
1058
|
+
return 0;
|
|
1059
|
+
}
|
|
1060
|
+
} // namespace
|
|
1061
|
+
|
|
1062
|
+
int Handler::http_end_request_headers(Stream *stream) {
|
|
1063
|
+
if (config.early_response) {
|
|
1064
|
+
if (start_response(stream) != 0) {
|
|
1065
|
+
return -1;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
shutdown_read(stream->stream_id, NGHTTP3_H3_NO_ERROR);
|
|
1069
|
+
}
|
|
1070
|
+
return 0;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
namespace {
|
|
1074
|
+
int http_end_stream(nghttp3_conn *conn, int64_t stream_id, void *user_data,
|
|
1075
|
+
void *stream_user_data) {
|
|
1076
|
+
auto h = static_cast<Handler *>(user_data);
|
|
1077
|
+
auto stream = static_cast<Stream *>(stream_user_data);
|
|
1078
|
+
if (h->http_end_stream(stream) != 0) {
|
|
1079
|
+
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
|
1080
|
+
}
|
|
1081
|
+
return 0;
|
|
1082
|
+
}
|
|
1083
|
+
} // namespace
|
|
1084
|
+
|
|
1085
|
+
int Handler::http_end_stream(Stream *stream) {
|
|
1086
|
+
if (!config.early_response) {
|
|
1087
|
+
return start_response(stream);
|
|
1088
|
+
}
|
|
1089
|
+
return 0;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
int Handler::start_response(Stream *stream) {
|
|
1093
|
+
return stream->start_response(httpconn_);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
namespace {
|
|
1097
|
+
int http_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
|
|
1098
|
+
uint64_t datalen, void *user_data,
|
|
1099
|
+
void *stream_user_data) {
|
|
1100
|
+
auto h = static_cast<Handler *>(user_data);
|
|
1101
|
+
auto stream = static_cast<Stream *>(stream_user_data);
|
|
1102
|
+
h->http_acked_stream_data(stream, datalen);
|
|
1103
|
+
return 0;
|
|
1104
|
+
}
|
|
1105
|
+
} // namespace
|
|
1106
|
+
|
|
1107
|
+
void Handler::http_acked_stream_data(Stream *stream, uint64_t datalen) {
|
|
1108
|
+
stream->http_acked_stream_data(datalen);
|
|
1109
|
+
|
|
1110
|
+
if (stream->dynresp && stream->dynbuflen < MAX_DYNBUFLEN - 16_k) {
|
|
1111
|
+
if (auto rv = nghttp3_conn_resume_stream(httpconn_, stream->stream_id);
|
|
1112
|
+
rv != 0) {
|
|
1113
|
+
// TODO Handle error
|
|
1114
|
+
std::cerr << "nghttp3_conn_resume_stream: " << nghttp3_strerror(rv)
|
|
1115
|
+
<< std::endl;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
namespace {
|
|
1121
|
+
int http_stream_close(nghttp3_conn *conn, int64_t stream_id,
|
|
1122
|
+
uint64_t app_error_code, void *conn_user_data,
|
|
1123
|
+
void *stream_user_data) {
|
|
1124
|
+
auto h = static_cast<Handler *>(conn_user_data);
|
|
1125
|
+
h->http_stream_close(stream_id, app_error_code);
|
|
1126
|
+
return 0;
|
|
1127
|
+
}
|
|
1128
|
+
} // namespace
|
|
1129
|
+
|
|
1130
|
+
void Handler::http_stream_close(int64_t stream_id, uint64_t app_error_code) {
|
|
1131
|
+
auto it = streams_.find(stream_id);
|
|
1132
|
+
if (it == std::end(streams_)) {
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (!config.quiet) {
|
|
1137
|
+
std::cerr << "HTTP stream " << stream_id << " closed with error code "
|
|
1138
|
+
<< app_error_code << std::endl;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
streams_.erase(it);
|
|
1142
|
+
|
|
1143
|
+
if (ngtcp2_is_bidi_stream(stream_id)) {
|
|
1144
|
+
assert(!ngtcp2_conn_is_local_stream(conn_, stream_id));
|
|
1145
|
+
ngtcp2_conn_extend_max_streams_bidi(conn_, 1);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
namespace {
|
|
1150
|
+
int http_stop_sending(nghttp3_conn *conn, int64_t stream_id,
|
|
1151
|
+
uint64_t app_error_code, void *user_data,
|
|
1152
|
+
void *stream_user_data) {
|
|
1153
|
+
auto h = static_cast<Handler *>(user_data);
|
|
1154
|
+
if (h->http_stop_sending(stream_id, app_error_code) != 0) {
|
|
1155
|
+
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
|
1156
|
+
}
|
|
1157
|
+
return 0;
|
|
1158
|
+
}
|
|
1159
|
+
} // namespace
|
|
1160
|
+
|
|
1161
|
+
int Handler::http_stop_sending(int64_t stream_id, uint64_t app_error_code) {
|
|
1162
|
+
if (auto rv =
|
|
1163
|
+
ngtcp2_conn_shutdown_stream_read(conn_, stream_id, app_error_code);
|
|
1164
|
+
rv != 0) {
|
|
1165
|
+
std::cerr << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv)
|
|
1166
|
+
<< std::endl;
|
|
1167
|
+
return -1;
|
|
1168
|
+
}
|
|
1169
|
+
return 0;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
namespace {
|
|
1173
|
+
int http_reset_stream(nghttp3_conn *conn, int64_t stream_id,
|
|
1174
|
+
uint64_t app_error_code, void *user_data,
|
|
1175
|
+
void *stream_user_data) {
|
|
1176
|
+
auto h = static_cast<Handler *>(user_data);
|
|
1177
|
+
if (h->http_reset_stream(stream_id, app_error_code) != 0) {
|
|
1178
|
+
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
|
1179
|
+
}
|
|
1180
|
+
return 0;
|
|
1181
|
+
}
|
|
1182
|
+
} // namespace
|
|
1183
|
+
|
|
1184
|
+
int Handler::http_reset_stream(int64_t stream_id, uint64_t app_error_code) {
|
|
1185
|
+
if (auto rv =
|
|
1186
|
+
ngtcp2_conn_shutdown_stream_write(conn_, stream_id, app_error_code);
|
|
1187
|
+
rv != 0) {
|
|
1188
|
+
std::cerr << "ngtcp2_conn_shutdown_stream_write: " << ngtcp2_strerror(rv)
|
|
1189
|
+
<< std::endl;
|
|
1190
|
+
return -1;
|
|
1191
|
+
}
|
|
1192
|
+
return 0;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
int Handler::setup_httpconn() {
|
|
1196
|
+
if (httpconn_) {
|
|
1197
|
+
return 0;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
if (ngtcp2_conn_get_max_local_streams_uni(conn_) < 3) {
|
|
1201
|
+
std::cerr << "peer does not allow at least 3 unidirectional streams."
|
|
1202
|
+
<< std::endl;
|
|
1203
|
+
return -1;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
nghttp3_callbacks callbacks{
|
|
1207
|
+
::http_acked_stream_data, // acked_stream_data
|
|
1208
|
+
::http_stream_close,
|
|
1209
|
+
::http_recv_data,
|
|
1210
|
+
::http_deferred_consume,
|
|
1211
|
+
::http_begin_request_headers,
|
|
1212
|
+
::http_recv_request_header,
|
|
1213
|
+
::http_end_request_headers,
|
|
1214
|
+
nullptr, // begin_trailers
|
|
1215
|
+
nullptr, // recv_trailer
|
|
1216
|
+
nullptr, // end_trailers
|
|
1217
|
+
::http_stop_sending,
|
|
1218
|
+
::http_end_stream,
|
|
1219
|
+
::http_reset_stream,
|
|
1220
|
+
};
|
|
1221
|
+
nghttp3_settings settings;
|
|
1222
|
+
nghttp3_settings_default(&settings);
|
|
1223
|
+
settings.qpack_max_dtable_capacity = 4096;
|
|
1224
|
+
settings.qpack_blocked_streams = 100;
|
|
1225
|
+
|
|
1226
|
+
auto mem = nghttp3_mem_default();
|
|
1227
|
+
|
|
1228
|
+
if (auto rv =
|
|
1229
|
+
nghttp3_conn_server_new(&httpconn_, &callbacks, &settings, mem, this);
|
|
1230
|
+
rv != 0) {
|
|
1231
|
+
std::cerr << "nghttp3_conn_server_new: " << nghttp3_strerror(rv)
|
|
1232
|
+
<< std::endl;
|
|
1233
|
+
return -1;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
auto params = ngtcp2_conn_get_local_transport_params(conn_);
|
|
1237
|
+
|
|
1238
|
+
nghttp3_conn_set_max_client_streams_bidi(httpconn_,
|
|
1239
|
+
params->initial_max_streams_bidi);
|
|
1240
|
+
|
|
1241
|
+
int64_t ctrl_stream_id;
|
|
1242
|
+
|
|
1243
|
+
if (auto rv = ngtcp2_conn_open_uni_stream(conn_, &ctrl_stream_id, nullptr);
|
|
1244
|
+
rv != 0) {
|
|
1245
|
+
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
|
|
1246
|
+
<< std::endl;
|
|
1247
|
+
return -1;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
if (auto rv = nghttp3_conn_bind_control_stream(httpconn_, ctrl_stream_id);
|
|
1251
|
+
rv != 0) {
|
|
1252
|
+
std::cerr << "nghttp3_conn_bind_control_stream: " << nghttp3_strerror(rv)
|
|
1253
|
+
<< std::endl;
|
|
1254
|
+
return -1;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
if (!config.quiet) {
|
|
1258
|
+
fprintf(stderr, "http: control stream=%" PRIx64 "\n", ctrl_stream_id);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
int64_t qpack_enc_stream_id, qpack_dec_stream_id;
|
|
1262
|
+
|
|
1263
|
+
if (auto rv =
|
|
1264
|
+
ngtcp2_conn_open_uni_stream(conn_, &qpack_enc_stream_id, nullptr);
|
|
1265
|
+
rv != 0) {
|
|
1266
|
+
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
|
|
1267
|
+
<< std::endl;
|
|
1268
|
+
return -1;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
if (auto rv =
|
|
1272
|
+
ngtcp2_conn_open_uni_stream(conn_, &qpack_dec_stream_id, nullptr);
|
|
1273
|
+
rv != 0) {
|
|
1274
|
+
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
|
|
1275
|
+
<< std::endl;
|
|
1276
|
+
return -1;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
if (auto rv = nghttp3_conn_bind_qpack_streams(httpconn_, qpack_enc_stream_id,
|
|
1280
|
+
qpack_dec_stream_id);
|
|
1281
|
+
rv != 0) {
|
|
1282
|
+
std::cerr << "nghttp3_conn_bind_qpack_streams: " << nghttp3_strerror(rv)
|
|
1283
|
+
<< std::endl;
|
|
1284
|
+
return -1;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
if (!config.quiet) {
|
|
1288
|
+
fprintf(stderr,
|
|
1289
|
+
"http: QPACK streams encoder=%" PRIx64 " decoder=%" PRIx64 "\n",
|
|
1290
|
+
qpack_enc_stream_id, qpack_dec_stream_id);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
return 0;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
namespace {
|
|
1297
|
+
int extend_max_stream_data(ngtcp2_conn *conn, int64_t stream_id,
|
|
1298
|
+
uint64_t max_data, void *user_data,
|
|
1299
|
+
void *stream_user_data) {
|
|
1300
|
+
auto h = static_cast<Handler *>(user_data);
|
|
1301
|
+
if (h->extend_max_stream_data(stream_id, max_data) != 0) {
|
|
1302
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
1303
|
+
}
|
|
1304
|
+
return 0;
|
|
1305
|
+
}
|
|
1306
|
+
} // namespace
|
|
1307
|
+
|
|
1308
|
+
int Handler::extend_max_stream_data(int64_t stream_id, uint64_t max_data) {
|
|
1309
|
+
if (auto rv = nghttp3_conn_unblock_stream(httpconn_, stream_id); rv != 0) {
|
|
1310
|
+
std::cerr << "nghttp3_conn_unblock_stream: " << nghttp3_strerror(rv)
|
|
1311
|
+
<< std::endl;
|
|
1312
|
+
return -1;
|
|
1313
|
+
}
|
|
1314
|
+
return 0;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
namespace {
|
|
1318
|
+
int recv_tx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level, void *user_data) {
|
|
1319
|
+
if (level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
|
|
1320
|
+
return 0;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
auto h = static_cast<Handler *>(user_data);
|
|
1324
|
+
if (h->setup_httpconn() != 0) {
|
|
1325
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
return 0;
|
|
1329
|
+
}
|
|
1330
|
+
} // namespace
|
|
1331
|
+
|
|
1332
|
+
namespace {
|
|
1333
|
+
void write_qlog(void *user_data, uint32_t flags, const void *data,
|
|
1334
|
+
size_t datalen) {
|
|
1335
|
+
auto h = static_cast<Handler *>(user_data);
|
|
1336
|
+
h->write_qlog(data, datalen);
|
|
1337
|
+
}
|
|
1338
|
+
} // namespace
|
|
1339
|
+
|
|
1340
|
+
void Handler::write_qlog(const void *data, size_t datalen) {
|
|
1341
|
+
assert(qlog_);
|
|
1342
|
+
fwrite(data, 1, datalen, qlog_);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
int Handler::init(const Endpoint &ep, const Address &local_addr,
|
|
1346
|
+
const sockaddr *sa, socklen_t salen, const ngtcp2_cid *dcid,
|
|
1347
|
+
const ngtcp2_cid *scid, const ngtcp2_cid *ocid,
|
|
1348
|
+
const uint8_t *token, size_t tokenlen, uint32_t version,
|
|
1349
|
+
TLSServerContext &tls_ctx) {
|
|
1350
|
+
auto callbacks = ngtcp2_callbacks{
|
|
1351
|
+
nullptr, // client_initial
|
|
1352
|
+
ngtcp2_crypto_recv_client_initial_cb,
|
|
1353
|
+
::recv_crypto_data,
|
|
1354
|
+
::handshake_completed,
|
|
1355
|
+
nullptr, // recv_version_negotiation
|
|
1356
|
+
ngtcp2_crypto_encrypt_cb,
|
|
1357
|
+
ngtcp2_crypto_decrypt_cb,
|
|
1358
|
+
do_hp_mask,
|
|
1359
|
+
::recv_stream_data,
|
|
1360
|
+
::acked_stream_data_offset,
|
|
1361
|
+
stream_open,
|
|
1362
|
+
stream_close,
|
|
1363
|
+
nullptr, // recv_stateless_reset
|
|
1364
|
+
nullptr, // recv_retry
|
|
1365
|
+
nullptr, // extend_max_streams_bidi
|
|
1366
|
+
nullptr, // extend_max_streams_uni
|
|
1367
|
+
rand,
|
|
1368
|
+
get_new_connection_id,
|
|
1369
|
+
remove_connection_id,
|
|
1370
|
+
::update_key,
|
|
1371
|
+
path_validation,
|
|
1372
|
+
nullptr, // select_preferred_addr
|
|
1373
|
+
::stream_reset,
|
|
1374
|
+
::extend_max_remote_streams_bidi,
|
|
1375
|
+
nullptr, // extend_max_remote_streams_uni
|
|
1376
|
+
::extend_max_stream_data,
|
|
1377
|
+
nullptr, // dcid_status
|
|
1378
|
+
nullptr, // handshake_confirmed
|
|
1379
|
+
nullptr, // recv_new_token
|
|
1380
|
+
ngtcp2_crypto_delete_crypto_aead_ctx_cb,
|
|
1381
|
+
ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
|
|
1382
|
+
nullptr, // recv_datagram
|
|
1383
|
+
nullptr, // ack_datagram
|
|
1384
|
+
nullptr, // lost_datagram
|
|
1385
|
+
ngtcp2_crypto_get_path_challenge_data_cb,
|
|
1386
|
+
stream_stop_sending,
|
|
1387
|
+
ngtcp2_crypto_version_negotiation_cb,
|
|
1388
|
+
nullptr, // recv_rx_key
|
|
1389
|
+
::recv_tx_key,
|
|
1390
|
+
};
|
|
1391
|
+
|
|
1392
|
+
scid_.datalen = NGTCP2_SV_SCIDLEN;
|
|
1393
|
+
if (util::generate_secure_random(scid_.data, scid_.datalen) != 0) {
|
|
1394
|
+
std::cerr << "Could not generate connection ID" << std::endl;
|
|
1395
|
+
return -1;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
ngtcp2_settings settings;
|
|
1399
|
+
ngtcp2_settings_default(&settings);
|
|
1400
|
+
settings.log_printf = config.quiet ? nullptr : debug::log_printf;
|
|
1401
|
+
settings.initial_ts = util::timestamp(loop_);
|
|
1402
|
+
settings.token = token;
|
|
1403
|
+
settings.tokenlen = tokenlen;
|
|
1404
|
+
settings.cc_algo = config.cc_algo;
|
|
1405
|
+
settings.initial_rtt = config.initial_rtt;
|
|
1406
|
+
settings.max_window = config.max_window;
|
|
1407
|
+
settings.max_stream_window = config.max_stream_window;
|
|
1408
|
+
settings.handshake_timeout = config.handshake_timeout;
|
|
1409
|
+
settings.no_pmtud = config.no_pmtud;
|
|
1410
|
+
settings.ack_thresh = config.ack_thresh;
|
|
1411
|
+
if (config.max_udp_payload_size) {
|
|
1412
|
+
settings.max_tx_udp_payload_size = config.max_udp_payload_size;
|
|
1413
|
+
settings.no_tx_udp_payload_size_shaping = 1;
|
|
1414
|
+
}
|
|
1415
|
+
if (!config.qlog_dir.empty()) {
|
|
1416
|
+
auto path = std::string{config.qlog_dir};
|
|
1417
|
+
path += '/';
|
|
1418
|
+
path += util::format_hex(scid_.data, scid_.datalen);
|
|
1419
|
+
path += ".sqlog";
|
|
1420
|
+
qlog_ = fopen(path.c_str(), "w");
|
|
1421
|
+
if (qlog_ == nullptr) {
|
|
1422
|
+
std::cerr << "Could not open qlog file " << std::quoted(path) << ": "
|
|
1423
|
+
<< strerror(errno) << std::endl;
|
|
1424
|
+
return -1;
|
|
1425
|
+
}
|
|
1426
|
+
settings.qlog.write = ::write_qlog;
|
|
1427
|
+
settings.qlog.odcid = *scid;
|
|
1428
|
+
}
|
|
1429
|
+
if (!config.preferred_versions.empty()) {
|
|
1430
|
+
settings.preferred_versions = config.preferred_versions.data();
|
|
1431
|
+
settings.preferred_versionslen = config.preferred_versions.size();
|
|
1432
|
+
}
|
|
1433
|
+
if (!config.available_versions.empty()) {
|
|
1434
|
+
settings.available_versions = config.available_versions.data();
|
|
1435
|
+
settings.available_versionslen = config.available_versions.size();
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
ngtcp2_transport_params params;
|
|
1439
|
+
ngtcp2_transport_params_default(¶ms);
|
|
1440
|
+
params.initial_max_stream_data_bidi_local = config.max_stream_data_bidi_local;
|
|
1441
|
+
params.initial_max_stream_data_bidi_remote =
|
|
1442
|
+
config.max_stream_data_bidi_remote;
|
|
1443
|
+
params.initial_max_stream_data_uni = config.max_stream_data_uni;
|
|
1444
|
+
params.initial_max_data = config.max_data;
|
|
1445
|
+
params.initial_max_streams_bidi = config.max_streams_bidi;
|
|
1446
|
+
params.initial_max_streams_uni = config.max_streams_uni;
|
|
1447
|
+
params.max_idle_timeout = config.timeout;
|
|
1448
|
+
params.stateless_reset_token_present = 1;
|
|
1449
|
+
params.active_connection_id_limit = 7;
|
|
1450
|
+
|
|
1451
|
+
if (ocid) {
|
|
1452
|
+
params.original_dcid = *ocid;
|
|
1453
|
+
params.retry_scid = *scid;
|
|
1454
|
+
params.retry_scid_present = 1;
|
|
1455
|
+
} else {
|
|
1456
|
+
params.original_dcid = *scid;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
if (util::generate_secure_random(params.stateless_reset_token,
|
|
1460
|
+
sizeof(params.stateless_reset_token)) != 0) {
|
|
1461
|
+
std::cerr << "Could not generate stateless reset token" << std::endl;
|
|
1462
|
+
return -1;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
if (config.preferred_ipv4_addr.len || config.preferred_ipv6_addr.len) {
|
|
1466
|
+
params.preferred_address_present = 1;
|
|
1467
|
+
|
|
1468
|
+
if (config.preferred_ipv4_addr.len) {
|
|
1469
|
+
params.preferred_address.ipv4 = config.preferred_ipv4_addr.su.in;
|
|
1470
|
+
params.preferred_address.ipv4_present = 1;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
if (config.preferred_ipv6_addr.len) {
|
|
1474
|
+
params.preferred_address.ipv6 = config.preferred_ipv6_addr.su.in6;
|
|
1475
|
+
params.preferred_address.ipv6_present = 1;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
auto &token = params.preferred_address.stateless_reset_token;
|
|
1479
|
+
if (util::generate_secure_random(token, sizeof(token)) != 0) {
|
|
1480
|
+
std::cerr << "Could not generate preferred address stateless reset token"
|
|
1481
|
+
<< std::endl;
|
|
1482
|
+
return -1;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
params.preferred_address.cid.datalen = NGTCP2_SV_SCIDLEN;
|
|
1486
|
+
if (util::generate_secure_random(params.preferred_address.cid.data,
|
|
1487
|
+
params.preferred_address.cid.datalen) !=
|
|
1488
|
+
0) {
|
|
1489
|
+
std::cerr << "Could not generate preferred address connection ID"
|
|
1490
|
+
<< std::endl;
|
|
1491
|
+
return -1;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
auto path = ngtcp2_path{
|
|
1496
|
+
{
|
|
1497
|
+
const_cast<sockaddr *>(&local_addr.su.sa),
|
|
1498
|
+
local_addr.len,
|
|
1499
|
+
},
|
|
1500
|
+
{
|
|
1501
|
+
const_cast<sockaddr *>(sa),
|
|
1502
|
+
salen,
|
|
1503
|
+
},
|
|
1504
|
+
const_cast<Endpoint *>(&ep),
|
|
1505
|
+
};
|
|
1506
|
+
if (auto rv =
|
|
1507
|
+
ngtcp2_conn_server_new(&conn_, dcid, &scid_, &path, version,
|
|
1508
|
+
&callbacks, &settings, ¶ms, nullptr, this);
|
|
1509
|
+
rv != 0) {
|
|
1510
|
+
std::cerr << "ngtcp2_conn_server_new: " << ngtcp2_strerror(rv) << std::endl;
|
|
1511
|
+
return -1;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
if (tls_session_.init(tls_ctx, this) != 0) {
|
|
1515
|
+
return -1;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
tls_session_.enable_keylog();
|
|
1519
|
+
|
|
1520
|
+
ngtcp2_conn_set_tls_native_handle(conn_, tls_session_.get_native_handle());
|
|
1521
|
+
|
|
1522
|
+
ev_io_set(&wev_, ep.fd, EV_WRITE);
|
|
1523
|
+
|
|
1524
|
+
return 0;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
int Handler::feed_data(const Endpoint &ep, const Address &local_addr,
|
|
1528
|
+
const sockaddr *sa, socklen_t salen,
|
|
1529
|
+
const ngtcp2_pkt_info *pi, uint8_t *data,
|
|
1530
|
+
size_t datalen) {
|
|
1531
|
+
auto path = ngtcp2_path{
|
|
1532
|
+
{
|
|
1533
|
+
const_cast<sockaddr *>(&local_addr.su.sa),
|
|
1534
|
+
local_addr.len,
|
|
1535
|
+
},
|
|
1536
|
+
{
|
|
1537
|
+
const_cast<sockaddr *>(sa),
|
|
1538
|
+
salen,
|
|
1539
|
+
},
|
|
1540
|
+
const_cast<Endpoint *>(&ep),
|
|
1541
|
+
};
|
|
1542
|
+
|
|
1543
|
+
if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, pi, data, datalen,
|
|
1544
|
+
util::timestamp(loop_));
|
|
1545
|
+
rv != 0) {
|
|
1546
|
+
std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
|
|
1547
|
+
switch (rv) {
|
|
1548
|
+
case NGTCP2_ERR_DRAINING:
|
|
1549
|
+
start_draining_period();
|
|
1550
|
+
return NETWORK_ERR_CLOSE_WAIT;
|
|
1551
|
+
case NGTCP2_ERR_RETRY:
|
|
1552
|
+
return NETWORK_ERR_RETRY;
|
|
1553
|
+
case NGTCP2_ERR_DROP_CONN:
|
|
1554
|
+
return NETWORK_ERR_DROP_CONN;
|
|
1555
|
+
case NGTCP2_ERR_CRYPTO:
|
|
1556
|
+
if (!last_error_.error_code) {
|
|
1557
|
+
ngtcp2_connection_close_error_set_transport_error_tls_alert(
|
|
1558
|
+
&last_error_, ngtcp2_conn_get_tls_alert(conn_), nullptr, 0);
|
|
1559
|
+
}
|
|
1560
|
+
break;
|
|
1561
|
+
default:
|
|
1562
|
+
if (!last_error_.error_code) {
|
|
1563
|
+
ngtcp2_connection_close_error_set_transport_error_liberr(
|
|
1564
|
+
&last_error_, rv, nullptr, 0);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
return handle_error();
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
return 0;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
int Handler::on_read(const Endpoint &ep, const Address &local_addr,
|
|
1574
|
+
const sockaddr *sa, socklen_t salen,
|
|
1575
|
+
const ngtcp2_pkt_info *pi, uint8_t *data, size_t datalen) {
|
|
1576
|
+
if (auto rv = feed_data(ep, local_addr, sa, salen, pi, data, datalen);
|
|
1577
|
+
rv != 0) {
|
|
1578
|
+
return rv;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
update_timer();
|
|
1582
|
+
|
|
1583
|
+
return 0;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
int Handler::handle_expiry() {
|
|
1587
|
+
auto now = util::timestamp(loop_);
|
|
1588
|
+
if (auto rv = ngtcp2_conn_handle_expiry(conn_, now); rv != 0) {
|
|
1589
|
+
std::cerr << "ngtcp2_conn_handle_expiry: " << ngtcp2_strerror(rv)
|
|
1590
|
+
<< std::endl;
|
|
1591
|
+
ngtcp2_connection_close_error_set_transport_error_liberr(&last_error_, rv,
|
|
1592
|
+
nullptr, 0);
|
|
1593
|
+
return handle_error();
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
return 0;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
int Handler::on_write() {
|
|
1600
|
+
if (ngtcp2_conn_is_in_closing_period(conn_) ||
|
|
1601
|
+
ngtcp2_conn_is_in_draining_period(conn_)) {
|
|
1602
|
+
return 0;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
if (tx_.send_blocked) {
|
|
1606
|
+
if (auto rv = send_blocked_packet(); rv != 0) {
|
|
1607
|
+
return rv;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
if (tx_.send_blocked) {
|
|
1611
|
+
return 0;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
ev_io_stop(loop_, &wev_);
|
|
1616
|
+
|
|
1617
|
+
if (auto rv = write_streams(); rv != 0) {
|
|
1618
|
+
return rv;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
update_timer();
|
|
1622
|
+
|
|
1623
|
+
return 0;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
int Handler::write_streams() {
|
|
1627
|
+
std::array<nghttp3_vec, 16> vec;
|
|
1628
|
+
ngtcp2_path_storage ps, prev_ps;
|
|
1629
|
+
uint32_t prev_ecn = 0;
|
|
1630
|
+
size_t pktcnt = 0;
|
|
1631
|
+
auto max_udp_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(conn_);
|
|
1632
|
+
auto path_max_udp_payload_size =
|
|
1633
|
+
ngtcp2_conn_get_path_max_tx_udp_payload_size(conn_);
|
|
1634
|
+
auto max_pktcnt = ngtcp2_conn_get_send_quantum(conn_) / max_udp_payload_size;
|
|
1635
|
+
uint8_t *bufpos = tx_.data.get();
|
|
1636
|
+
ngtcp2_pkt_info pi;
|
|
1637
|
+
size_t gso_size = 0;
|
|
1638
|
+
auto ts = util::timestamp(loop_);
|
|
1639
|
+
|
|
1640
|
+
ngtcp2_path_storage_zero(&ps);
|
|
1641
|
+
ngtcp2_path_storage_zero(&prev_ps);
|
|
1642
|
+
|
|
1643
|
+
max_pktcnt = std::min(max_pktcnt, static_cast<size_t>(config.max_gso_dgrams));
|
|
1644
|
+
|
|
1645
|
+
for (;;) {
|
|
1646
|
+
int64_t stream_id = -1;
|
|
1647
|
+
int fin = 0;
|
|
1648
|
+
nghttp3_ssize sveccnt = 0;
|
|
1649
|
+
|
|
1650
|
+
if (httpconn_ && ngtcp2_conn_get_max_data_left(conn_)) {
|
|
1651
|
+
sveccnt = nghttp3_conn_writev_stream(httpconn_, &stream_id, &fin,
|
|
1652
|
+
vec.data(), vec.size());
|
|
1653
|
+
if (sveccnt < 0) {
|
|
1654
|
+
std::cerr << "nghttp3_conn_writev_stream: " << nghttp3_strerror(sveccnt)
|
|
1655
|
+
<< std::endl;
|
|
1656
|
+
ngtcp2_connection_close_error_set_application_error(
|
|
1657
|
+
&last_error_, nghttp3_err_infer_quic_app_error_code(sveccnt),
|
|
1658
|
+
nullptr, 0);
|
|
1659
|
+
return handle_error();
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
ngtcp2_ssize ndatalen;
|
|
1664
|
+
auto v = vec.data();
|
|
1665
|
+
auto vcnt = static_cast<size_t>(sveccnt);
|
|
1666
|
+
|
|
1667
|
+
uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_MORE;
|
|
1668
|
+
if (fin) {
|
|
1669
|
+
flags |= NGTCP2_WRITE_STREAM_FLAG_FIN;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
auto nwrite = ngtcp2_conn_writev_stream(
|
|
1673
|
+
conn_, &ps.path, &pi, bufpos, max_udp_payload_size, &ndatalen, flags,
|
|
1674
|
+
stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt, ts);
|
|
1675
|
+
if (nwrite < 0) {
|
|
1676
|
+
switch (nwrite) {
|
|
1677
|
+
case NGTCP2_ERR_STREAM_DATA_BLOCKED:
|
|
1678
|
+
assert(ndatalen == -1);
|
|
1679
|
+
nghttp3_conn_block_stream(httpconn_, stream_id);
|
|
1680
|
+
continue;
|
|
1681
|
+
case NGTCP2_ERR_STREAM_SHUT_WR:
|
|
1682
|
+
assert(ndatalen == -1);
|
|
1683
|
+
nghttp3_conn_shutdown_stream_write(httpconn_, stream_id);
|
|
1684
|
+
continue;
|
|
1685
|
+
case NGTCP2_ERR_WRITE_MORE:
|
|
1686
|
+
assert(ndatalen >= 0);
|
|
1687
|
+
if (auto rv =
|
|
1688
|
+
nghttp3_conn_add_write_offset(httpconn_, stream_id, ndatalen);
|
|
1689
|
+
rv != 0) {
|
|
1690
|
+
std::cerr << "nghttp3_conn_add_write_offset: " << nghttp3_strerror(rv)
|
|
1691
|
+
<< std::endl;
|
|
1692
|
+
ngtcp2_connection_close_error_set_application_error(
|
|
1693
|
+
&last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr,
|
|
1694
|
+
0);
|
|
1695
|
+
return handle_error();
|
|
1696
|
+
}
|
|
1697
|
+
continue;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
assert(ndatalen == -1);
|
|
1701
|
+
|
|
1702
|
+
std::cerr << "ngtcp2_conn_writev_stream: " << ngtcp2_strerror(nwrite)
|
|
1703
|
+
<< std::endl;
|
|
1704
|
+
ngtcp2_connection_close_error_set_transport_error_liberr(
|
|
1705
|
+
&last_error_, nwrite, nullptr, 0);
|
|
1706
|
+
return handle_error();
|
|
1707
|
+
} else if (ndatalen >= 0) {
|
|
1708
|
+
if (auto rv =
|
|
1709
|
+
nghttp3_conn_add_write_offset(httpconn_, stream_id, ndatalen);
|
|
1710
|
+
rv != 0) {
|
|
1711
|
+
std::cerr << "nghttp3_conn_add_write_offset: " << nghttp3_strerror(rv)
|
|
1712
|
+
<< std::endl;
|
|
1713
|
+
ngtcp2_connection_close_error_set_application_error(
|
|
1714
|
+
&last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr,
|
|
1715
|
+
0);
|
|
1716
|
+
return handle_error();
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
if (nwrite == 0) {
|
|
1721
|
+
if (bufpos - tx_.data.get()) {
|
|
1722
|
+
auto &ep = *static_cast<Endpoint *>(prev_ps.path.user_data);
|
|
1723
|
+
auto data = tx_.data.get();
|
|
1724
|
+
auto datalen = bufpos - data;
|
|
1725
|
+
|
|
1726
|
+
if (auto [nsent, rv] = server_->send_packet(
|
|
1727
|
+
ep, no_gso_, prev_ps.path.local, prev_ps.path.remote, prev_ecn,
|
|
1728
|
+
data, datalen, gso_size);
|
|
1729
|
+
rv != NETWORK_ERR_OK) {
|
|
1730
|
+
assert(NETWORK_ERR_SEND_BLOCKED == rv);
|
|
1731
|
+
|
|
1732
|
+
on_send_blocked(ep, prev_ps.path.local, prev_ps.path.remote, prev_ecn,
|
|
1733
|
+
data + nsent, datalen - nsent, gso_size);
|
|
1734
|
+
|
|
1735
|
+
start_wev_endpoint(ep);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
// We are congestion limited.
|
|
1740
|
+
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
|
|
1741
|
+
return 0;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
bufpos += nwrite;
|
|
1745
|
+
|
|
1746
|
+
if (pktcnt == 0) {
|
|
1747
|
+
ngtcp2_path_copy(&prev_ps.path, &ps.path);
|
|
1748
|
+
prev_ecn = pi.ecn;
|
|
1749
|
+
gso_size = nwrite;
|
|
1750
|
+
} else if (!ngtcp2_path_eq(&prev_ps.path, &ps.path) || prev_ecn != pi.ecn ||
|
|
1751
|
+
static_cast<size_t>(nwrite) > gso_size ||
|
|
1752
|
+
(gso_size > path_max_udp_payload_size &&
|
|
1753
|
+
static_cast<size_t>(nwrite) != gso_size)) {
|
|
1754
|
+
auto &ep = *static_cast<Endpoint *>(prev_ps.path.user_data);
|
|
1755
|
+
auto data = tx_.data.get();
|
|
1756
|
+
auto datalen = bufpos - data - nwrite;
|
|
1757
|
+
|
|
1758
|
+
if (auto [nsent, rv] = server_->send_packet(
|
|
1759
|
+
ep, no_gso_, prev_ps.path.local, prev_ps.path.remote, prev_ecn,
|
|
1760
|
+
data, datalen, gso_size);
|
|
1761
|
+
rv != 0) {
|
|
1762
|
+
assert(NETWORK_ERR_SEND_BLOCKED == rv);
|
|
1763
|
+
|
|
1764
|
+
on_send_blocked(ep, prev_ps.path.local, prev_ps.path.remote, prev_ecn,
|
|
1765
|
+
data + nsent, datalen - nsent, gso_size);
|
|
1766
|
+
|
|
1767
|
+
on_send_blocked(*static_cast<Endpoint *>(ps.path.user_data),
|
|
1768
|
+
ps.path.local, ps.path.remote, pi.ecn, bufpos - nwrite,
|
|
1769
|
+
nwrite, 0);
|
|
1770
|
+
|
|
1771
|
+
start_wev_endpoint(ep);
|
|
1772
|
+
} else {
|
|
1773
|
+
auto &ep = *static_cast<Endpoint *>(ps.path.user_data);
|
|
1774
|
+
auto data = bufpos - nwrite;
|
|
1775
|
+
|
|
1776
|
+
if (auto [nsent, rv] =
|
|
1777
|
+
server_->send_packet(ep, no_gso_, ps.path.local, ps.path.remote,
|
|
1778
|
+
pi.ecn, data, nwrite, nwrite);
|
|
1779
|
+
rv != 0) {
|
|
1780
|
+
assert(nsent == 0);
|
|
1781
|
+
assert(NETWORK_ERR_SEND_BLOCKED == rv);
|
|
1782
|
+
|
|
1783
|
+
on_send_blocked(ep, ps.path.local, ps.path.remote, pi.ecn, data,
|
|
1784
|
+
nwrite, 0);
|
|
1785
|
+
|
|
1786
|
+
start_wev_endpoint(ep);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
|
|
1791
|
+
return 0;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
if (++pktcnt == max_pktcnt || static_cast<size_t>(nwrite) < gso_size) {
|
|
1795
|
+
auto &ep = *static_cast<Endpoint *>(ps.path.user_data);
|
|
1796
|
+
auto data = tx_.data.get();
|
|
1797
|
+
auto datalen = bufpos - data;
|
|
1798
|
+
|
|
1799
|
+
if (auto [nsent, rv] =
|
|
1800
|
+
server_->send_packet(ep, no_gso_, ps.path.local, ps.path.remote,
|
|
1801
|
+
pi.ecn, data, datalen, gso_size);
|
|
1802
|
+
rv != 0) {
|
|
1803
|
+
assert(NETWORK_ERR_SEND_BLOCKED == rv);
|
|
1804
|
+
|
|
1805
|
+
on_send_blocked(ep, ps.path.local, ps.path.remote, pi.ecn, data + nsent,
|
|
1806
|
+
datalen - nsent, gso_size);
|
|
1807
|
+
|
|
1808
|
+
start_wev_endpoint(ep);
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
|
|
1812
|
+
return 0;
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
void Handler::on_send_blocked(Endpoint &ep, const ngtcp2_addr &local_addr,
|
|
1818
|
+
const ngtcp2_addr &remote_addr, unsigned int ecn,
|
|
1819
|
+
const uint8_t *data, size_t datalen,
|
|
1820
|
+
size_t gso_size) {
|
|
1821
|
+
assert(tx_.num_blocked || !tx_.send_blocked);
|
|
1822
|
+
assert(tx_.num_blocked < 2);
|
|
1823
|
+
|
|
1824
|
+
tx_.send_blocked = true;
|
|
1825
|
+
|
|
1826
|
+
auto &p = tx_.blocked[tx_.num_blocked++];
|
|
1827
|
+
|
|
1828
|
+
memcpy(&p.local_addr.su, local_addr.addr, local_addr.addrlen);
|
|
1829
|
+
memcpy(&p.remote_addr.su, remote_addr.addr, remote_addr.addrlen);
|
|
1830
|
+
|
|
1831
|
+
p.local_addr.len = local_addr.addrlen;
|
|
1832
|
+
p.remote_addr.len = remote_addr.addrlen;
|
|
1833
|
+
p.endpoint = &ep;
|
|
1834
|
+
p.ecn = ecn;
|
|
1835
|
+
p.data = data;
|
|
1836
|
+
p.datalen = datalen;
|
|
1837
|
+
p.gso_size = gso_size;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
void Handler::start_wev_endpoint(const Endpoint &ep) {
|
|
1841
|
+
// We do not close ep.fd, so we can expect that each Endpoint has
|
|
1842
|
+
// unique fd.
|
|
1843
|
+
if (ep.fd != wev_.fd) {
|
|
1844
|
+
if (ev_is_active(&wev_)) {
|
|
1845
|
+
ev_io_stop(loop_, &wev_);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
ev_io_set(&wev_, ep.fd, EV_WRITE);
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
ev_io_start(loop_, &wev_);
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
int Handler::send_blocked_packet() {
|
|
1855
|
+
assert(tx_.send_blocked);
|
|
1856
|
+
|
|
1857
|
+
for (; tx_.num_blocked_sent < tx_.num_blocked; ++tx_.num_blocked_sent) {
|
|
1858
|
+
auto &p = tx_.blocked[tx_.num_blocked_sent];
|
|
1859
|
+
|
|
1860
|
+
ngtcp2_addr local_addr{
|
|
1861
|
+
.addr = &p.local_addr.su.sa,
|
|
1862
|
+
.addrlen = p.local_addr.len,
|
|
1863
|
+
};
|
|
1864
|
+
ngtcp2_addr remote_addr{
|
|
1865
|
+
.addr = &p.remote_addr.su.sa,
|
|
1866
|
+
.addrlen = p.remote_addr.len,
|
|
1867
|
+
};
|
|
1868
|
+
|
|
1869
|
+
auto [nsent, rv] =
|
|
1870
|
+
server_->send_packet(*p.endpoint, no_gso_, local_addr, remote_addr,
|
|
1871
|
+
p.ecn, p.data, p.datalen, p.gso_size);
|
|
1872
|
+
if (rv != 0) {
|
|
1873
|
+
assert(NETWORK_ERR_SEND_BLOCKED == rv);
|
|
1874
|
+
|
|
1875
|
+
p.data += nsent;
|
|
1876
|
+
p.datalen -= nsent;
|
|
1877
|
+
|
|
1878
|
+
start_wev_endpoint(*p.endpoint);
|
|
1879
|
+
|
|
1880
|
+
return 0;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
tx_.send_blocked = false;
|
|
1885
|
+
tx_.num_blocked = 0;
|
|
1886
|
+
tx_.num_blocked_sent = 0;
|
|
1887
|
+
|
|
1888
|
+
return 0;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
void Handler::signal_write() { ev_io_start(loop_, &wev_); }
|
|
1892
|
+
|
|
1893
|
+
void Handler::start_draining_period() {
|
|
1894
|
+
ev_io_stop(loop_, &wev_);
|
|
1895
|
+
|
|
1896
|
+
ev_set_cb(&timer_, close_waitcb);
|
|
1897
|
+
timer_.repeat =
|
|
1898
|
+
static_cast<ev_tstamp>(ngtcp2_conn_get_pto(conn_)) / NGTCP2_SECONDS * 3;
|
|
1899
|
+
ev_timer_again(loop_, &timer_);
|
|
1900
|
+
|
|
1901
|
+
if (!config.quiet) {
|
|
1902
|
+
std::cerr << "Draining period has started (" << timer_.repeat << " seconds)"
|
|
1903
|
+
<< std::endl;
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
int Handler::start_closing_period() {
|
|
1908
|
+
if (!conn_ || ngtcp2_conn_is_in_closing_period(conn_) ||
|
|
1909
|
+
ngtcp2_conn_is_in_draining_period(conn_)) {
|
|
1910
|
+
return 0;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
ev_io_stop(loop_, &wev_);
|
|
1914
|
+
|
|
1915
|
+
ev_set_cb(&timer_, close_waitcb);
|
|
1916
|
+
timer_.repeat =
|
|
1917
|
+
static_cast<ev_tstamp>(ngtcp2_conn_get_pto(conn_)) / NGTCP2_SECONDS * 3;
|
|
1918
|
+
ev_timer_again(loop_, &timer_);
|
|
1919
|
+
|
|
1920
|
+
if (!config.quiet) {
|
|
1921
|
+
std::cerr << "Closing period has started (" << timer_.repeat << " seconds)"
|
|
1922
|
+
<< std::endl;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
conn_closebuf_ = std::make_unique<Buffer>(NGTCP2_MAX_UDP_PAYLOAD_SIZE);
|
|
1926
|
+
|
|
1927
|
+
ngtcp2_path_storage ps;
|
|
1928
|
+
|
|
1929
|
+
ngtcp2_path_storage_zero(&ps);
|
|
1930
|
+
|
|
1931
|
+
ngtcp2_pkt_info pi;
|
|
1932
|
+
auto n = ngtcp2_conn_write_connection_close(
|
|
1933
|
+
conn_, &ps.path, &pi, conn_closebuf_->wpos(), conn_closebuf_->left(),
|
|
1934
|
+
&last_error_, util::timestamp(loop_));
|
|
1935
|
+
if (n < 0) {
|
|
1936
|
+
std::cerr << "ngtcp2_conn_write_connection_close: " << ngtcp2_strerror(n)
|
|
1937
|
+
<< std::endl;
|
|
1938
|
+
return -1;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
if (n == 0) {
|
|
1942
|
+
return 0;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
conn_closebuf_->push(n);
|
|
1946
|
+
|
|
1947
|
+
return 0;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
int Handler::handle_error() {
|
|
1951
|
+
if (last_error_.type ==
|
|
1952
|
+
NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE) {
|
|
1953
|
+
return -1;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
if (start_closing_period() != 0) {
|
|
1957
|
+
return -1;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
if (ngtcp2_conn_is_in_draining_period(conn_)) {
|
|
1961
|
+
return NETWORK_ERR_CLOSE_WAIT;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
if (auto rv = send_conn_close(); rv != NETWORK_ERR_OK) {
|
|
1965
|
+
return rv;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
return NETWORK_ERR_CLOSE_WAIT;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
int Handler::send_conn_close() {
|
|
1972
|
+
if (!config.quiet) {
|
|
1973
|
+
std::cerr << "Closing Period: TX CONNECTION_CLOSE" << std::endl;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
assert(conn_closebuf_ && conn_closebuf_->size());
|
|
1977
|
+
assert(conn_);
|
|
1978
|
+
assert(!ngtcp2_conn_is_in_draining_period(conn_));
|
|
1979
|
+
|
|
1980
|
+
auto path = ngtcp2_conn_get_path(conn_);
|
|
1981
|
+
|
|
1982
|
+
return server_->send_packet(
|
|
1983
|
+
*static_cast<Endpoint *>(path->user_data), path->local, path->remote,
|
|
1984
|
+
/* ecn = */ 0, conn_closebuf_->rpos(), conn_closebuf_->size());
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
void Handler::update_timer() {
|
|
1988
|
+
auto expiry = ngtcp2_conn_get_expiry(conn_);
|
|
1989
|
+
auto now = util::timestamp(loop_);
|
|
1990
|
+
|
|
1991
|
+
if (expiry <= now) {
|
|
1992
|
+
if (!config.quiet) {
|
|
1993
|
+
auto t = static_cast<ev_tstamp>(now - expiry) / NGTCP2_SECONDS;
|
|
1994
|
+
std::cerr << "Timer has already expired: " << std::fixed << t << "s"
|
|
1995
|
+
<< std::defaultfloat << std::endl;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
ev_feed_event(loop_, &timer_, EV_TIMER);
|
|
1999
|
+
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
auto t = static_cast<ev_tstamp>(expiry - now) / NGTCP2_SECONDS;
|
|
2004
|
+
if (!config.quiet) {
|
|
2005
|
+
std::cerr << "Set timer=" << std::fixed << t << "s" << std::defaultfloat
|
|
2006
|
+
<< std::endl;
|
|
2007
|
+
}
|
|
2008
|
+
timer_.repeat = t;
|
|
2009
|
+
ev_timer_again(loop_, &timer_);
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
int Handler::recv_stream_data(uint32_t flags, int64_t stream_id,
|
|
2013
|
+
const uint8_t *data, size_t datalen) {
|
|
2014
|
+
if (!config.quiet && !config.no_quic_dump) {
|
|
2015
|
+
debug::print_stream_data(stream_id, data, datalen);
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
if (!httpconn_) {
|
|
2019
|
+
return 0;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
auto nconsumed = nghttp3_conn_read_stream(
|
|
2023
|
+
httpconn_, stream_id, data, datalen, flags & NGTCP2_STREAM_DATA_FLAG_FIN);
|
|
2024
|
+
if (nconsumed < 0) {
|
|
2025
|
+
std::cerr << "nghttp3_conn_read_stream: " << nghttp3_strerror(nconsumed)
|
|
2026
|
+
<< std::endl;
|
|
2027
|
+
ngtcp2_connection_close_error_set_application_error(
|
|
2028
|
+
&last_error_, nghttp3_err_infer_quic_app_error_code(nconsumed), nullptr,
|
|
2029
|
+
0);
|
|
2030
|
+
return -1;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
ngtcp2_conn_extend_max_stream_offset(conn_, stream_id, nconsumed);
|
|
2034
|
+
ngtcp2_conn_extend_max_offset(conn_, nconsumed);
|
|
2035
|
+
|
|
2036
|
+
return 0;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
int Handler::update_key(uint8_t *rx_secret, uint8_t *tx_secret,
|
|
2040
|
+
ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
|
|
2041
|
+
ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
|
|
2042
|
+
const uint8_t *current_rx_secret,
|
|
2043
|
+
const uint8_t *current_tx_secret, size_t secretlen) {
|
|
2044
|
+
auto crypto_ctx = ngtcp2_conn_get_crypto_ctx(conn_);
|
|
2045
|
+
auto aead = &crypto_ctx->aead;
|
|
2046
|
+
auto keylen = ngtcp2_crypto_aead_keylen(aead);
|
|
2047
|
+
auto ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
|
|
2048
|
+
|
|
2049
|
+
++nkey_update_;
|
|
2050
|
+
|
|
2051
|
+
std::array<uint8_t, 64> rx_key, tx_key;
|
|
2052
|
+
|
|
2053
|
+
if (ngtcp2_crypto_update_key(conn_, rx_secret, tx_secret, rx_aead_ctx,
|
|
2054
|
+
rx_key.data(), rx_iv, tx_aead_ctx, tx_key.data(),
|
|
2055
|
+
tx_iv, current_rx_secret, current_tx_secret,
|
|
2056
|
+
secretlen) != 0) {
|
|
2057
|
+
return -1;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
if (!config.quiet && config.show_secret) {
|
|
2061
|
+
std::cerr << "application_traffic rx secret " << nkey_update_ << std::endl;
|
|
2062
|
+
debug::print_secrets(rx_secret, secretlen, rx_key.data(), keylen, rx_iv,
|
|
2063
|
+
ivlen);
|
|
2064
|
+
std::cerr << "application_traffic tx secret " << nkey_update_ << std::endl;
|
|
2065
|
+
debug::print_secrets(tx_secret, secretlen, tx_key.data(), keylen, tx_iv,
|
|
2066
|
+
ivlen);
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
return 0;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
Server *Handler::server() const { return server_; }
|
|
2073
|
+
|
|
2074
|
+
int Handler::on_stream_close(int64_t stream_id, uint64_t app_error_code) {
|
|
2075
|
+
if (!config.quiet) {
|
|
2076
|
+
std::cerr << "QUIC stream " << stream_id << " closed" << std::endl;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
if (httpconn_) {
|
|
2080
|
+
if (app_error_code == 0) {
|
|
2081
|
+
app_error_code = NGHTTP3_H3_NO_ERROR;
|
|
2082
|
+
}
|
|
2083
|
+
auto rv = nghttp3_conn_close_stream(httpconn_, stream_id, app_error_code);
|
|
2084
|
+
switch (rv) {
|
|
2085
|
+
case 0:
|
|
2086
|
+
break;
|
|
2087
|
+
case NGHTTP3_ERR_STREAM_NOT_FOUND:
|
|
2088
|
+
if (ngtcp2_is_bidi_stream(stream_id)) {
|
|
2089
|
+
assert(!ngtcp2_conn_is_local_stream(conn_, stream_id));
|
|
2090
|
+
ngtcp2_conn_extend_max_streams_bidi(conn_, 1);
|
|
2091
|
+
}
|
|
2092
|
+
break;
|
|
2093
|
+
default:
|
|
2094
|
+
std::cerr << "nghttp3_conn_close_stream: " << nghttp3_strerror(rv)
|
|
2095
|
+
<< std::endl;
|
|
2096
|
+
ngtcp2_connection_close_error_set_application_error(
|
|
2097
|
+
&last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr, 0);
|
|
2098
|
+
return -1;
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
return 0;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
void Handler::shutdown_read(int64_t stream_id, int app_error_code) {
|
|
2106
|
+
ngtcp2_conn_shutdown_stream_read(conn_, stream_id, app_error_code);
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
namespace {
|
|
2110
|
+
void sreadcb(struct ev_loop *loop, ev_io *w, int revents) {
|
|
2111
|
+
auto ep = static_cast<Endpoint *>(w->data);
|
|
2112
|
+
|
|
2113
|
+
ep->server->on_read(*ep);
|
|
2114
|
+
}
|
|
2115
|
+
} // namespace
|
|
2116
|
+
|
|
2117
|
+
namespace {
|
|
2118
|
+
void siginthandler(struct ev_loop *loop, ev_signal *watcher, int revents) {
|
|
2119
|
+
ev_break(loop, EVBREAK_ALL);
|
|
2120
|
+
}
|
|
2121
|
+
} // namespace
|
|
2122
|
+
|
|
2123
|
+
Server::Server(struct ev_loop *loop, TLSServerContext &tls_ctx)
|
|
2124
|
+
: loop_(loop), tls_ctx_(tls_ctx) {
|
|
2125
|
+
ev_signal_init(&sigintev_, siginthandler, SIGINT);
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
Server::~Server() {
|
|
2129
|
+
disconnect();
|
|
2130
|
+
close();
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
void Server::disconnect() {
|
|
2134
|
+
config.tx_loss_prob = 0;
|
|
2135
|
+
|
|
2136
|
+
for (auto &ep : endpoints_) {
|
|
2137
|
+
ev_io_stop(loop_, &ep.rev);
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
ev_signal_stop(loop_, &sigintev_);
|
|
2141
|
+
|
|
2142
|
+
while (!handlers_.empty()) {
|
|
2143
|
+
auto it = std::begin(handlers_);
|
|
2144
|
+
auto &h = (*it).second;
|
|
2145
|
+
|
|
2146
|
+
h->handle_error();
|
|
2147
|
+
|
|
2148
|
+
remove(h);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
void Server::close() {
|
|
2153
|
+
for (auto &ep : endpoints_) {
|
|
2154
|
+
::close(ep.fd);
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
endpoints_.clear();
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
namespace {
|
|
2161
|
+
int create_sock(Address &local_addr, const char *addr, const char *port,
|
|
2162
|
+
int family) {
|
|
2163
|
+
addrinfo hints{};
|
|
2164
|
+
addrinfo *res, *rp;
|
|
2165
|
+
int val = 1;
|
|
2166
|
+
|
|
2167
|
+
hints.ai_family = family;
|
|
2168
|
+
hints.ai_socktype = SOCK_DGRAM;
|
|
2169
|
+
hints.ai_flags = AI_PASSIVE;
|
|
2170
|
+
|
|
2171
|
+
if (strcmp(addr, "*") == 0) {
|
|
2172
|
+
addr = nullptr;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
if (auto rv = getaddrinfo(addr, port, &hints, &res); rv != 0) {
|
|
2176
|
+
std::cerr << "getaddrinfo: " << gai_strerror(rv) << std::endl;
|
|
2177
|
+
return -1;
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
auto res_d = defer(freeaddrinfo, res);
|
|
2181
|
+
|
|
2182
|
+
int fd = -1;
|
|
2183
|
+
|
|
2184
|
+
for (rp = res; rp; rp = rp->ai_next) {
|
|
2185
|
+
fd = util::create_nonblock_socket(rp->ai_family, rp->ai_socktype,
|
|
2186
|
+
rp->ai_protocol);
|
|
2187
|
+
if (fd == -1) {
|
|
2188
|
+
continue;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
if (rp->ai_family == AF_INET6) {
|
|
2192
|
+
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
|
|
2193
|
+
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
2194
|
+
close(fd);
|
|
2195
|
+
continue;
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val,
|
|
2199
|
+
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
2200
|
+
close(fd);
|
|
2201
|
+
continue;
|
|
2202
|
+
}
|
|
2203
|
+
} else if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &val,
|
|
2204
|
+
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
2205
|
+
close(fd);
|
|
2206
|
+
continue;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
|
|
2210
|
+
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
2211
|
+
close(fd);
|
|
2212
|
+
continue;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
fd_set_recv_ecn(fd, rp->ai_family);
|
|
2216
|
+
fd_set_ip_mtu_discover(fd, rp->ai_family);
|
|
2217
|
+
fd_set_ip_dontfrag(fd, family);
|
|
2218
|
+
|
|
2219
|
+
if (bind(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
|
|
2220
|
+
break;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
close(fd);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
if (!rp) {
|
|
2227
|
+
std::cerr << "Could not bind" << std::endl;
|
|
2228
|
+
return -1;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
socklen_t len = sizeof(local_addr.su.storage);
|
|
2232
|
+
if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
|
|
2233
|
+
std::cerr << "getsockname: " << strerror(errno) << std::endl;
|
|
2234
|
+
close(fd);
|
|
2235
|
+
return -1;
|
|
2236
|
+
}
|
|
2237
|
+
local_addr.len = len;
|
|
2238
|
+
local_addr.ifindex = 0;
|
|
2239
|
+
|
|
2240
|
+
return fd;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
} // namespace
|
|
2244
|
+
|
|
2245
|
+
namespace {
|
|
2246
|
+
int add_endpoint(std::vector<Endpoint> &endpoints, const char *addr,
|
|
2247
|
+
const char *port, int af) {
|
|
2248
|
+
Address dest;
|
|
2249
|
+
auto fd = create_sock(dest, addr, port, af);
|
|
2250
|
+
if (fd == -1) {
|
|
2251
|
+
return -1;
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
endpoints.emplace_back();
|
|
2255
|
+
auto &ep = endpoints.back();
|
|
2256
|
+
ep.addr = dest;
|
|
2257
|
+
ep.fd = fd;
|
|
2258
|
+
ev_io_init(&ep.rev, sreadcb, 0, EV_READ);
|
|
2259
|
+
|
|
2260
|
+
return 0;
|
|
2261
|
+
}
|
|
2262
|
+
} // namespace
|
|
2263
|
+
|
|
2264
|
+
namespace {
|
|
2265
|
+
int add_endpoint(std::vector<Endpoint> &endpoints, const Address &addr) {
|
|
2266
|
+
auto fd = util::create_nonblock_socket(addr.su.sa.sa_family, SOCK_DGRAM, 0);
|
|
2267
|
+
if (fd == -1) {
|
|
2268
|
+
std::cerr << "socket: " << strerror(errno) << std::endl;
|
|
2269
|
+
return -1;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
int val = 1;
|
|
2273
|
+
if (addr.su.sa.sa_family == AF_INET6) {
|
|
2274
|
+
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
|
|
2275
|
+
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
2276
|
+
std::cerr << "setsockopt: " << strerror(errno) << std::endl;
|
|
2277
|
+
close(fd);
|
|
2278
|
+
return -1;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val,
|
|
2282
|
+
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
2283
|
+
std::cerr << "setsockopt: " << strerror(errno) << std::endl;
|
|
2284
|
+
close(fd);
|
|
2285
|
+
return -1;
|
|
2286
|
+
}
|
|
2287
|
+
} else if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &val,
|
|
2288
|
+
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
2289
|
+
std::cerr << "setsockopt: " << strerror(errno) << std::endl;
|
|
2290
|
+
close(fd);
|
|
2291
|
+
return -1;
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
|
|
2295
|
+
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
2296
|
+
close(fd);
|
|
2297
|
+
return -1;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
fd_set_recv_ecn(fd, addr.su.sa.sa_family);
|
|
2301
|
+
fd_set_ip_mtu_discover(fd, addr.su.sa.sa_family);
|
|
2302
|
+
fd_set_ip_dontfrag(fd, addr.su.sa.sa_family);
|
|
2303
|
+
|
|
2304
|
+
if (bind(fd, &addr.su.sa, addr.len) == -1) {
|
|
2305
|
+
std::cerr << "bind: " << strerror(errno) << std::endl;
|
|
2306
|
+
close(fd);
|
|
2307
|
+
return -1;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
endpoints.emplace_back(Endpoint{});
|
|
2311
|
+
auto &ep = endpoints.back();
|
|
2312
|
+
ep.addr = addr;
|
|
2313
|
+
ep.fd = fd;
|
|
2314
|
+
ev_io_init(&ep.rev, sreadcb, 0, EV_READ);
|
|
2315
|
+
|
|
2316
|
+
return 0;
|
|
2317
|
+
}
|
|
2318
|
+
} // namespace
|
|
2319
|
+
|
|
2320
|
+
int Server::init(const char *addr, const char *port) {
|
|
2321
|
+
endpoints_.reserve(4);
|
|
2322
|
+
|
|
2323
|
+
auto ready = false;
|
|
2324
|
+
if (!util::numeric_host(addr, AF_INET6) &&
|
|
2325
|
+
add_endpoint(endpoints_, addr, port, AF_INET) == 0) {
|
|
2326
|
+
ready = true;
|
|
2327
|
+
}
|
|
2328
|
+
if (!util::numeric_host(addr, AF_INET) &&
|
|
2329
|
+
add_endpoint(endpoints_, addr, port, AF_INET6) == 0) {
|
|
2330
|
+
ready = true;
|
|
2331
|
+
}
|
|
2332
|
+
if (!ready) {
|
|
2333
|
+
return -1;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
if (config.preferred_ipv4_addr.len &&
|
|
2337
|
+
add_endpoint(endpoints_, config.preferred_ipv4_addr) != 0) {
|
|
2338
|
+
return -1;
|
|
2339
|
+
}
|
|
2340
|
+
if (config.preferred_ipv6_addr.len &&
|
|
2341
|
+
add_endpoint(endpoints_, config.preferred_ipv6_addr) != 0) {
|
|
2342
|
+
return -1;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
for (auto &ep : endpoints_) {
|
|
2346
|
+
ep.server = this;
|
|
2347
|
+
ep.rev.data = &ep;
|
|
2348
|
+
|
|
2349
|
+
ev_io_set(&ep.rev, ep.fd, EV_READ);
|
|
2350
|
+
|
|
2351
|
+
ev_io_start(loop_, &ep.rev);
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
ev_signal_start(loop_, &sigintev_);
|
|
2355
|
+
|
|
2356
|
+
return 0;
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
int Server::on_read(Endpoint &ep) {
|
|
2360
|
+
sockaddr_union su;
|
|
2361
|
+
std::array<uint8_t, 64_k> buf;
|
|
2362
|
+
ngtcp2_pkt_hd hd;
|
|
2363
|
+
size_t pktcnt = 0;
|
|
2364
|
+
ngtcp2_pkt_info pi;
|
|
2365
|
+
|
|
2366
|
+
iovec msg_iov;
|
|
2367
|
+
msg_iov.iov_base = buf.data();
|
|
2368
|
+
msg_iov.iov_len = buf.size();
|
|
2369
|
+
|
|
2370
|
+
msghdr msg{};
|
|
2371
|
+
msg.msg_name = &su;
|
|
2372
|
+
msg.msg_iov = &msg_iov;
|
|
2373
|
+
msg.msg_iovlen = 1;
|
|
2374
|
+
|
|
2375
|
+
uint8_t
|
|
2376
|
+
msg_ctrl[CMSG_SPACE(sizeof(uint8_t)) + CMSG_SPACE(sizeof(in6_pktinfo))];
|
|
2377
|
+
msg.msg_control = msg_ctrl;
|
|
2378
|
+
|
|
2379
|
+
for (; pktcnt < 10;) {
|
|
2380
|
+
msg.msg_namelen = sizeof(su);
|
|
2381
|
+
msg.msg_controllen = sizeof(msg_ctrl);
|
|
2382
|
+
|
|
2383
|
+
auto nread = recvmsg(ep.fd, &msg, 0);
|
|
2384
|
+
if (nread == -1) {
|
|
2385
|
+
if (!(errno == EAGAIN || errno == ENOTCONN)) {
|
|
2386
|
+
std::cerr << "recvmsg: " << strerror(errno) << std::endl;
|
|
2387
|
+
}
|
|
2388
|
+
return 0;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
++pktcnt;
|
|
2392
|
+
|
|
2393
|
+
pi.ecn = msghdr_get_ecn(&msg, su.storage.ss_family);
|
|
2394
|
+
auto local_addr = msghdr_get_local_addr(&msg, su.storage.ss_family);
|
|
2395
|
+
if (!local_addr) {
|
|
2396
|
+
std::cerr << "Unable to obtain local address" << std::endl;
|
|
2397
|
+
continue;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
set_port(*local_addr, ep.addr);
|
|
2401
|
+
|
|
2402
|
+
if (!config.quiet) {
|
|
2403
|
+
std::array<char, IF_NAMESIZE> ifname;
|
|
2404
|
+
std::cerr << "Received packet: local="
|
|
2405
|
+
<< util::straddr(&local_addr->su.sa, local_addr->len)
|
|
2406
|
+
<< " remote=" << util::straddr(&su.sa, msg.msg_namelen)
|
|
2407
|
+
<< " if=" << if_indextoname(local_addr->ifindex, ifname.data())
|
|
2408
|
+
<< " ecn=0x" << std::hex << pi.ecn << std::dec << " " << nread
|
|
2409
|
+
<< " bytes" << std::endl;
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
if (debug::packet_lost(config.rx_loss_prob)) {
|
|
2413
|
+
if (!config.quiet) {
|
|
2414
|
+
std::cerr << "** Simulated incoming packet loss **" << std::endl;
|
|
2415
|
+
}
|
|
2416
|
+
continue;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
if (nread == 0) {
|
|
2420
|
+
continue;
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
ngtcp2_version_cid vc;
|
|
2424
|
+
|
|
2425
|
+
switch (auto rv = ngtcp2_pkt_decode_version_cid(&vc, buf.data(), nread,
|
|
2426
|
+
NGTCP2_SV_SCIDLEN);
|
|
2427
|
+
rv) {
|
|
2428
|
+
case 0:
|
|
2429
|
+
break;
|
|
2430
|
+
case NGTCP2_ERR_VERSION_NEGOTIATION:
|
|
2431
|
+
send_version_negotiation(vc.version, vc.scid, vc.scidlen, vc.dcid,
|
|
2432
|
+
vc.dcidlen, ep, *local_addr, &su.sa,
|
|
2433
|
+
msg.msg_namelen);
|
|
2434
|
+
continue;
|
|
2435
|
+
default:
|
|
2436
|
+
std::cerr << "Could not decode version and CID from QUIC packet header: "
|
|
2437
|
+
<< ngtcp2_strerror(rv) << std::endl;
|
|
2438
|
+
continue;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
auto dcid_key = util::make_cid_key(vc.dcid, vc.dcidlen);
|
|
2442
|
+
|
|
2443
|
+
auto handler_it = handlers_.find(dcid_key);
|
|
2444
|
+
if (handler_it == std::end(handlers_)) {
|
|
2445
|
+
if (auto rv = ngtcp2_accept(&hd, buf.data(), nread); rv != 0) {
|
|
2446
|
+
if (!config.quiet) {
|
|
2447
|
+
std::cerr << "Unexpected packet received: length=" << nread
|
|
2448
|
+
<< std::endl;
|
|
2449
|
+
}
|
|
2450
|
+
continue;
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
ngtcp2_cid ocid;
|
|
2454
|
+
ngtcp2_cid *pocid = nullptr;
|
|
2455
|
+
|
|
2456
|
+
assert(hd.type == NGTCP2_PKT_INITIAL);
|
|
2457
|
+
|
|
2458
|
+
if (config.validate_addr || hd.tokenlen) {
|
|
2459
|
+
std::cerr << "Perform stateless address validation" << std::endl;
|
|
2460
|
+
if (hd.tokenlen == 0) {
|
|
2461
|
+
send_retry(&hd, ep, *local_addr, &su.sa, msg.msg_namelen, nread * 3);
|
|
2462
|
+
continue;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
if (hd.token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY &&
|
|
2466
|
+
hd.dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) {
|
|
2467
|
+
send_stateless_connection_close(&hd, ep, *local_addr, &su.sa,
|
|
2468
|
+
msg.msg_namelen);
|
|
2469
|
+
continue;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
switch (hd.token[0]) {
|
|
2473
|
+
case NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY:
|
|
2474
|
+
if (verify_retry_token(&ocid, &hd, &su.sa, msg.msg_namelen) != 0) {
|
|
2475
|
+
send_stateless_connection_close(&hd, ep, *local_addr, &su.sa,
|
|
2476
|
+
msg.msg_namelen);
|
|
2477
|
+
continue;
|
|
2478
|
+
}
|
|
2479
|
+
pocid = &ocid;
|
|
2480
|
+
break;
|
|
2481
|
+
case NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR:
|
|
2482
|
+
if (verify_token(&hd, &su.sa, msg.msg_namelen) != 0) {
|
|
2483
|
+
if (config.validate_addr) {
|
|
2484
|
+
send_retry(&hd, ep, *local_addr, &su.sa, msg.msg_namelen,
|
|
2485
|
+
nread * 3);
|
|
2486
|
+
continue;
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
hd.token = nullptr;
|
|
2490
|
+
hd.tokenlen = 0;
|
|
2491
|
+
}
|
|
2492
|
+
break;
|
|
2493
|
+
default:
|
|
2494
|
+
if (!config.quiet) {
|
|
2495
|
+
std::cerr << "Ignore unrecognized token" << std::endl;
|
|
2496
|
+
}
|
|
2497
|
+
if (config.validate_addr) {
|
|
2498
|
+
send_retry(&hd, ep, *local_addr, &su.sa, msg.msg_namelen,
|
|
2499
|
+
nread * 3);
|
|
2500
|
+
continue;
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
hd.token = nullptr;
|
|
2504
|
+
hd.tokenlen = 0;
|
|
2505
|
+
break;
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
auto h = std::make_unique<Handler>(loop_, this);
|
|
2510
|
+
if (h->init(ep, *local_addr, &su.sa, msg.msg_namelen, &hd.scid, &hd.dcid,
|
|
2511
|
+
pocid, hd.token, hd.tokenlen, hd.version, tls_ctx_) != 0) {
|
|
2512
|
+
continue;
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
switch (h->on_read(ep, *local_addr, &su.sa, msg.msg_namelen, &pi,
|
|
2516
|
+
buf.data(), nread)) {
|
|
2517
|
+
case 0:
|
|
2518
|
+
break;
|
|
2519
|
+
case NETWORK_ERR_RETRY:
|
|
2520
|
+
send_retry(&hd, ep, *local_addr, &su.sa, msg.msg_namelen, nread * 3);
|
|
2521
|
+
continue;
|
|
2522
|
+
default:
|
|
2523
|
+
continue;
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
switch (h->on_write()) {
|
|
2527
|
+
case 0:
|
|
2528
|
+
break;
|
|
2529
|
+
default:
|
|
2530
|
+
continue;
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
std::array<ngtcp2_cid, 2> scids;
|
|
2534
|
+
auto conn = h->conn();
|
|
2535
|
+
|
|
2536
|
+
auto num_scid = ngtcp2_conn_get_num_scid(conn);
|
|
2537
|
+
|
|
2538
|
+
assert(num_scid <= scids.size());
|
|
2539
|
+
|
|
2540
|
+
ngtcp2_conn_get_scid(conn, scids.data());
|
|
2541
|
+
|
|
2542
|
+
for (size_t i = 0; i < num_scid; ++i) {
|
|
2543
|
+
associate_cid(&scids[i], h.get());
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
handlers_.emplace(dcid_key, h.get());
|
|
2547
|
+
|
|
2548
|
+
h.release();
|
|
2549
|
+
|
|
2550
|
+
continue;
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
auto h = (*handler_it).second;
|
|
2554
|
+
auto conn = h->conn();
|
|
2555
|
+
if (ngtcp2_conn_is_in_closing_period(conn)) {
|
|
2556
|
+
// TODO do exponential backoff.
|
|
2557
|
+
switch (h->send_conn_close()) {
|
|
2558
|
+
case 0:
|
|
2559
|
+
break;
|
|
2560
|
+
default:
|
|
2561
|
+
remove(h);
|
|
2562
|
+
}
|
|
2563
|
+
continue;
|
|
2564
|
+
}
|
|
2565
|
+
if (ngtcp2_conn_is_in_draining_period(conn)) {
|
|
2566
|
+
continue;
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
if (auto rv = h->on_read(ep, *local_addr, &su.sa, msg.msg_namelen, &pi,
|
|
2570
|
+
buf.data(), nread);
|
|
2571
|
+
rv != 0) {
|
|
2572
|
+
if (rv != NETWORK_ERR_CLOSE_WAIT) {
|
|
2573
|
+
remove(h);
|
|
2574
|
+
}
|
|
2575
|
+
continue;
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
h->signal_write();
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
return 0;
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
namespace {
|
|
2585
|
+
uint32_t generate_reserved_version(const sockaddr *sa, socklen_t salen,
|
|
2586
|
+
uint32_t version) {
|
|
2587
|
+
uint32_t h = 0x811C9DC5u;
|
|
2588
|
+
const uint8_t *p = (const uint8_t *)sa;
|
|
2589
|
+
const uint8_t *ep = p + salen;
|
|
2590
|
+
for (; p != ep; ++p) {
|
|
2591
|
+
h ^= *p;
|
|
2592
|
+
h *= 0x01000193u;
|
|
2593
|
+
}
|
|
2594
|
+
version = htonl(version);
|
|
2595
|
+
p = (const uint8_t *)&version;
|
|
2596
|
+
ep = p + sizeof(version);
|
|
2597
|
+
for (; p != ep; ++p) {
|
|
2598
|
+
h ^= *p;
|
|
2599
|
+
h *= 0x01000193u;
|
|
2600
|
+
}
|
|
2601
|
+
h &= 0xf0f0f0f0u;
|
|
2602
|
+
h |= 0x0a0a0a0au;
|
|
2603
|
+
return h;
|
|
2604
|
+
}
|
|
2605
|
+
} // namespace
|
|
2606
|
+
|
|
2607
|
+
int Server::send_version_negotiation(uint32_t version, const uint8_t *dcid,
|
|
2608
|
+
size_t dcidlen, const uint8_t *scid,
|
|
2609
|
+
size_t scidlen, Endpoint &ep,
|
|
2610
|
+
const Address &local_addr,
|
|
2611
|
+
const sockaddr *sa, socklen_t salen) {
|
|
2612
|
+
Buffer buf{NGTCP2_MAX_UDP_PAYLOAD_SIZE};
|
|
2613
|
+
std::array<uint32_t, 1 + max_preferred_versionslen> sv;
|
|
2614
|
+
|
|
2615
|
+
auto p = std::begin(sv);
|
|
2616
|
+
|
|
2617
|
+
*p++ = generate_reserved_version(sa, salen, version);
|
|
2618
|
+
|
|
2619
|
+
if (config.preferred_versions.empty()) {
|
|
2620
|
+
*p++ = NGTCP2_PROTO_VER_V1;
|
|
2621
|
+
} else {
|
|
2622
|
+
for (auto v : config.preferred_versions) {
|
|
2623
|
+
*p++ = v;
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
auto nwrite = ngtcp2_pkt_write_version_negotiation(
|
|
2628
|
+
buf.wpos(), buf.left(), std::uniform_int_distribution<uint8_t>()(randgen),
|
|
2629
|
+
dcid, dcidlen, scid, scidlen, sv.data(), p - std::begin(sv));
|
|
2630
|
+
if (nwrite < 0) {
|
|
2631
|
+
std::cerr << "ngtcp2_pkt_write_version_negotiation: "
|
|
2632
|
+
<< ngtcp2_strerror(nwrite) << std::endl;
|
|
2633
|
+
return -1;
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
buf.push(nwrite);
|
|
2637
|
+
|
|
2638
|
+
ngtcp2_addr laddr{
|
|
2639
|
+
const_cast<sockaddr *>(&local_addr.su.sa),
|
|
2640
|
+
local_addr.len,
|
|
2641
|
+
};
|
|
2642
|
+
ngtcp2_addr raddr{
|
|
2643
|
+
const_cast<sockaddr *>(sa),
|
|
2644
|
+
salen,
|
|
2645
|
+
};
|
|
2646
|
+
|
|
2647
|
+
if (send_packet(ep, laddr, raddr, /* ecn = */ 0, buf.rpos(), buf.size()) !=
|
|
2648
|
+
NETWORK_ERR_OK) {
|
|
2649
|
+
return -1;
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
return 0;
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
int Server::send_retry(const ngtcp2_pkt_hd *chd, Endpoint &ep,
|
|
2656
|
+
const Address &local_addr, const sockaddr *sa,
|
|
2657
|
+
socklen_t salen, size_t max_pktlen) {
|
|
2658
|
+
std::array<char, NI_MAXHOST> host;
|
|
2659
|
+
std::array<char, NI_MAXSERV> port;
|
|
2660
|
+
|
|
2661
|
+
if (auto rv = getnameinfo(sa, salen, host.data(), host.size(), port.data(),
|
|
2662
|
+
port.size(), NI_NUMERICHOST | NI_NUMERICSERV);
|
|
2663
|
+
rv != 0) {
|
|
2664
|
+
std::cerr << "getnameinfo: " << gai_strerror(rv) << std::endl;
|
|
2665
|
+
return -1;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
if (!config.quiet) {
|
|
2669
|
+
std::cerr << "Sending Retry packet to [" << host.data()
|
|
2670
|
+
<< "]:" << port.data() << std::endl;
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
ngtcp2_cid scid;
|
|
2674
|
+
|
|
2675
|
+
scid.datalen = NGTCP2_SV_SCIDLEN;
|
|
2676
|
+
if (util::generate_secure_random(scid.data, scid.datalen) != 0) {
|
|
2677
|
+
return -1;
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
std::array<uint8_t, NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN> token;
|
|
2681
|
+
|
|
2682
|
+
auto t = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
2683
|
+
std::chrono::system_clock::now().time_since_epoch())
|
|
2684
|
+
.count();
|
|
2685
|
+
|
|
2686
|
+
auto tokenlen = ngtcp2_crypto_generate_retry_token(
|
|
2687
|
+
token.data(), config.static_secret.data(), config.static_secret.size(),
|
|
2688
|
+
chd->version, sa, salen, &scid, &chd->dcid, t);
|
|
2689
|
+
if (tokenlen < 0) {
|
|
2690
|
+
return -1;
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
if (!config.quiet) {
|
|
2694
|
+
std::cerr << "Generated address validation token:" << std::endl;
|
|
2695
|
+
util::hexdump(stderr, token.data(), tokenlen);
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
Buffer buf{
|
|
2699
|
+
std::min(static_cast<size_t>(NGTCP2_MAX_UDP_PAYLOAD_SIZE), max_pktlen)};
|
|
2700
|
+
|
|
2701
|
+
auto nwrite = ngtcp2_crypto_write_retry(buf.wpos(), buf.left(), chd->version,
|
|
2702
|
+
&chd->scid, &scid, &chd->dcid,
|
|
2703
|
+
token.data(), tokenlen);
|
|
2704
|
+
if (nwrite < 0) {
|
|
2705
|
+
std::cerr << "ngtcp2_crypto_write_retry failed" << std::endl;
|
|
2706
|
+
return -1;
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
buf.push(nwrite);
|
|
2710
|
+
|
|
2711
|
+
ngtcp2_addr laddr{
|
|
2712
|
+
const_cast<sockaddr *>(&local_addr.su.sa),
|
|
2713
|
+
local_addr.len,
|
|
2714
|
+
};
|
|
2715
|
+
ngtcp2_addr raddr{
|
|
2716
|
+
const_cast<sockaddr *>(sa),
|
|
2717
|
+
salen,
|
|
2718
|
+
};
|
|
2719
|
+
|
|
2720
|
+
if (send_packet(ep, laddr, raddr, /* ecn = */ 0, buf.rpos(), buf.size()) !=
|
|
2721
|
+
NETWORK_ERR_OK) {
|
|
2722
|
+
return -1;
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
return 0;
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
int Server::send_stateless_connection_close(const ngtcp2_pkt_hd *chd,
|
|
2729
|
+
Endpoint &ep,
|
|
2730
|
+
const Address &local_addr,
|
|
2731
|
+
const sockaddr *sa,
|
|
2732
|
+
socklen_t salen) {
|
|
2733
|
+
Buffer buf{NGTCP2_MAX_UDP_PAYLOAD_SIZE};
|
|
2734
|
+
|
|
2735
|
+
auto nwrite = ngtcp2_crypto_write_connection_close(
|
|
2736
|
+
buf.wpos(), buf.left(), chd->version, &chd->scid, &chd->dcid,
|
|
2737
|
+
NGTCP2_INVALID_TOKEN, nullptr, 0);
|
|
2738
|
+
if (nwrite < 0) {
|
|
2739
|
+
std::cerr << "ngtcp2_crypto_write_connection_close failed" << std::endl;
|
|
2740
|
+
return -1;
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
buf.push(nwrite);
|
|
2744
|
+
|
|
2745
|
+
ngtcp2_addr laddr{
|
|
2746
|
+
const_cast<sockaddr *>(&local_addr.su.sa),
|
|
2747
|
+
local_addr.len,
|
|
2748
|
+
};
|
|
2749
|
+
ngtcp2_addr raddr{
|
|
2750
|
+
const_cast<sockaddr *>(sa),
|
|
2751
|
+
salen,
|
|
2752
|
+
};
|
|
2753
|
+
|
|
2754
|
+
if (send_packet(ep, laddr, raddr, /* ecn = */ 0, buf.rpos(), buf.size()) !=
|
|
2755
|
+
NETWORK_ERR_OK) {
|
|
2756
|
+
return -1;
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
return 0;
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
int Server::verify_retry_token(ngtcp2_cid *ocid, const ngtcp2_pkt_hd *hd,
|
|
2763
|
+
const sockaddr *sa, socklen_t salen) {
|
|
2764
|
+
std::array<char, NI_MAXHOST> host;
|
|
2765
|
+
std::array<char, NI_MAXSERV> port;
|
|
2766
|
+
|
|
2767
|
+
if (auto rv = getnameinfo(sa, salen, host.data(), host.size(), port.data(),
|
|
2768
|
+
port.size(), NI_NUMERICHOST | NI_NUMERICSERV);
|
|
2769
|
+
rv != 0) {
|
|
2770
|
+
std::cerr << "getnameinfo: " << gai_strerror(rv) << std::endl;
|
|
2771
|
+
return -1;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
if (!config.quiet) {
|
|
2775
|
+
std::cerr << "Verifying Retry token from [" << host.data()
|
|
2776
|
+
<< "]:" << port.data() << std::endl;
|
|
2777
|
+
util::hexdump(stderr, hd->token, hd->tokenlen);
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
auto t = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
2781
|
+
std::chrono::system_clock::now().time_since_epoch())
|
|
2782
|
+
.count();
|
|
2783
|
+
|
|
2784
|
+
if (ngtcp2_crypto_verify_retry_token(
|
|
2785
|
+
ocid, hd->token, hd->tokenlen, config.static_secret.data(),
|
|
2786
|
+
config.static_secret.size(), hd->version, sa, salen, &hd->dcid,
|
|
2787
|
+
10 * NGTCP2_SECONDS, t) != 0) {
|
|
2788
|
+
std::cerr << "Could not verify Retry token" << std::endl;
|
|
2789
|
+
|
|
2790
|
+
return -1;
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
if (!config.quiet) {
|
|
2794
|
+
std::cerr << "Token was successfully validated" << std::endl;
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
return 0;
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
int Server::verify_token(const ngtcp2_pkt_hd *hd, const sockaddr *sa,
|
|
2801
|
+
socklen_t salen) {
|
|
2802
|
+
std::array<char, NI_MAXHOST> host;
|
|
2803
|
+
std::array<char, NI_MAXSERV> port;
|
|
2804
|
+
|
|
2805
|
+
if (auto rv = getnameinfo(sa, salen, host.data(), host.size(), port.data(),
|
|
2806
|
+
port.size(), NI_NUMERICHOST | NI_NUMERICSERV);
|
|
2807
|
+
rv != 0) {
|
|
2808
|
+
std::cerr << "getnameinfo: " << gai_strerror(rv) << std::endl;
|
|
2809
|
+
return -1;
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
if (!config.quiet) {
|
|
2813
|
+
std::cerr << "Verifying token from [" << host.data() << "]:" << port.data()
|
|
2814
|
+
<< std::endl;
|
|
2815
|
+
util::hexdump(stderr, hd->token, hd->tokenlen);
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
auto t = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
2819
|
+
std::chrono::system_clock::now().time_since_epoch())
|
|
2820
|
+
.count();
|
|
2821
|
+
|
|
2822
|
+
if (ngtcp2_crypto_verify_regular_token(hd->token, hd->tokenlen,
|
|
2823
|
+
config.static_secret.data(),
|
|
2824
|
+
config.static_secret.size(), sa, salen,
|
|
2825
|
+
3600 * NGTCP2_SECONDS, t) != 0) {
|
|
2826
|
+
if (!config.quiet) {
|
|
2827
|
+
std::cerr << "Could not verify token" << std::endl;
|
|
2828
|
+
}
|
|
2829
|
+
return -1;
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
if (!config.quiet) {
|
|
2833
|
+
std::cerr << "Token was successfully validated" << std::endl;
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
return 0;
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
int Server::send_packet(Endpoint &ep, const ngtcp2_addr &local_addr,
|
|
2840
|
+
const ngtcp2_addr &remote_addr, unsigned int ecn,
|
|
2841
|
+
const uint8_t *data, size_t datalen) {
|
|
2842
|
+
auto no_gso = false;
|
|
2843
|
+
auto [_, rv] = send_packet(ep, no_gso, local_addr, remote_addr, ecn, data,
|
|
2844
|
+
datalen, datalen);
|
|
2845
|
+
|
|
2846
|
+
return rv;
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
std::pair<size_t, int>
|
|
2850
|
+
Server::send_packet(Endpoint &ep, bool &no_gso, const ngtcp2_addr &local_addr,
|
|
2851
|
+
const ngtcp2_addr &remote_addr, unsigned int ecn,
|
|
2852
|
+
const uint8_t *data, size_t datalen, size_t gso_size) {
|
|
2853
|
+
assert(gso_size);
|
|
2854
|
+
|
|
2855
|
+
if (debug::packet_lost(config.tx_loss_prob)) {
|
|
2856
|
+
if (!config.quiet) {
|
|
2857
|
+
std::cerr << "** Simulated outgoing packet loss **" << std::endl;
|
|
2858
|
+
}
|
|
2859
|
+
return {0, NETWORK_ERR_OK};
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
if (no_gso && datalen > gso_size) {
|
|
2863
|
+
size_t nsent = 0;
|
|
2864
|
+
|
|
2865
|
+
for (auto p = data; p < data + datalen; p += gso_size) {
|
|
2866
|
+
auto len = std::min(gso_size, static_cast<size_t>(data + datalen - p));
|
|
2867
|
+
|
|
2868
|
+
auto [n, rv] =
|
|
2869
|
+
send_packet(ep, no_gso, local_addr, remote_addr, ecn, p, len, len);
|
|
2870
|
+
if (rv != 0) {
|
|
2871
|
+
return {nsent, rv};
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
nsent += n;
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
return {nsent, 0};
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
iovec msg_iov;
|
|
2881
|
+
msg_iov.iov_base = const_cast<uint8_t *>(data);
|
|
2882
|
+
msg_iov.iov_len = datalen;
|
|
2883
|
+
|
|
2884
|
+
msghdr msg{};
|
|
2885
|
+
msg.msg_name = const_cast<sockaddr *>(remote_addr.addr);
|
|
2886
|
+
msg.msg_namelen = remote_addr.addrlen;
|
|
2887
|
+
msg.msg_iov = &msg_iov;
|
|
2888
|
+
msg.msg_iovlen = 1;
|
|
2889
|
+
|
|
2890
|
+
uint8_t
|
|
2891
|
+
msg_ctrl[CMSG_SPACE(sizeof(uint16_t)) + CMSG_SPACE(sizeof(in6_pktinfo))];
|
|
2892
|
+
|
|
2893
|
+
memset(msg_ctrl, 0, sizeof(msg_ctrl));
|
|
2894
|
+
|
|
2895
|
+
msg.msg_control = msg_ctrl;
|
|
2896
|
+
msg.msg_controllen = sizeof(msg_ctrl);
|
|
2897
|
+
|
|
2898
|
+
size_t controllen = 0;
|
|
2899
|
+
|
|
2900
|
+
auto cm = CMSG_FIRSTHDR(&msg);
|
|
2901
|
+
|
|
2902
|
+
switch (local_addr.addr->sa_family) {
|
|
2903
|
+
case AF_INET: {
|
|
2904
|
+
controllen += CMSG_SPACE(sizeof(in_pktinfo));
|
|
2905
|
+
cm->cmsg_level = IPPROTO_IP;
|
|
2906
|
+
cm->cmsg_type = IP_PKTINFO;
|
|
2907
|
+
cm->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
|
|
2908
|
+
auto pktinfo = reinterpret_cast<in_pktinfo *>(CMSG_DATA(cm));
|
|
2909
|
+
memset(pktinfo, 0, sizeof(in_pktinfo));
|
|
2910
|
+
auto addrin = reinterpret_cast<sockaddr_in *>(local_addr.addr);
|
|
2911
|
+
pktinfo->ipi_spec_dst = addrin->sin_addr;
|
|
2912
|
+
break;
|
|
2913
|
+
}
|
|
2914
|
+
case AF_INET6: {
|
|
2915
|
+
controllen += CMSG_SPACE(sizeof(in6_pktinfo));
|
|
2916
|
+
cm->cmsg_level = IPPROTO_IPV6;
|
|
2917
|
+
cm->cmsg_type = IPV6_PKTINFO;
|
|
2918
|
+
cm->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
|
|
2919
|
+
auto pktinfo = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cm));
|
|
2920
|
+
memset(pktinfo, 0, sizeof(in6_pktinfo));
|
|
2921
|
+
auto addrin = reinterpret_cast<sockaddr_in6 *>(local_addr.addr);
|
|
2922
|
+
pktinfo->ipi6_addr = addrin->sin6_addr;
|
|
2923
|
+
break;
|
|
2924
|
+
}
|
|
2925
|
+
default:
|
|
2926
|
+
assert(0);
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
#ifdef UDP_SEGMENT
|
|
2930
|
+
if (datalen > gso_size) {
|
|
2931
|
+
controllen += CMSG_SPACE(sizeof(uint16_t));
|
|
2932
|
+
cm = CMSG_NXTHDR(&msg, cm);
|
|
2933
|
+
cm->cmsg_level = SOL_UDP;
|
|
2934
|
+
cm->cmsg_type = UDP_SEGMENT;
|
|
2935
|
+
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
|
|
2936
|
+
*(reinterpret_cast<uint16_t *>(CMSG_DATA(cm))) = gso_size;
|
|
2937
|
+
}
|
|
2938
|
+
#endif // UDP_SEGMENT
|
|
2939
|
+
|
|
2940
|
+
msg.msg_controllen = controllen;
|
|
2941
|
+
|
|
2942
|
+
if (ep.ecn != ecn) {
|
|
2943
|
+
ep.ecn = ecn;
|
|
2944
|
+
fd_set_ecn(ep.fd, ep.addr.su.storage.ss_family, ecn);
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
ssize_t nwrite = 0;
|
|
2948
|
+
|
|
2949
|
+
do {
|
|
2950
|
+
nwrite = sendmsg(ep.fd, &msg, 0);
|
|
2951
|
+
} while (nwrite == -1 && errno == EINTR);
|
|
2952
|
+
|
|
2953
|
+
if (nwrite == -1) {
|
|
2954
|
+
switch (errno) {
|
|
2955
|
+
case EAGAIN:
|
|
2956
|
+
#if EAGAIN != EWOULDBLOCK
|
|
2957
|
+
case EWOULDBLOCK:
|
|
2958
|
+
#endif // EAGAIN != EWOULDBLOCK
|
|
2959
|
+
return {0, NETWORK_ERR_SEND_BLOCKED};
|
|
2960
|
+
#ifdef UDP_SEGMENT
|
|
2961
|
+
case EIO:
|
|
2962
|
+
if (datalen > gso_size) {
|
|
2963
|
+
// GSO failure; send each packet in a separate sendmsg call.
|
|
2964
|
+
std::cerr << "sendmsg: disabling GSO due to " << strerror(errno)
|
|
2965
|
+
<< std::endl;
|
|
2966
|
+
|
|
2967
|
+
no_gso = true;
|
|
2968
|
+
|
|
2969
|
+
return send_packet(ep, no_gso, local_addr, remote_addr, ecn, data,
|
|
2970
|
+
datalen, gso_size);
|
|
2971
|
+
}
|
|
2972
|
+
break;
|
|
2973
|
+
#endif // UDP_SEGMENT
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
std::cerr << "sendmsg: " << strerror(errno) << std::endl;
|
|
2977
|
+
// TODO We have packet which is expected to fail to send (e.g.,
|
|
2978
|
+
// path validation to old path).
|
|
2979
|
+
return {0, NETWORK_ERR_OK};
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
if (!config.quiet) {
|
|
2983
|
+
std::cerr << "Sent packet: local="
|
|
2984
|
+
<< util::straddr(local_addr.addr, local_addr.addrlen)
|
|
2985
|
+
<< " remote="
|
|
2986
|
+
<< util::straddr(remote_addr.addr, remote_addr.addrlen)
|
|
2987
|
+
<< " ecn=0x" << std::hex << ecn << std::dec << " " << nwrite
|
|
2988
|
+
<< " bytes" << std::endl;
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
return {nwrite, NETWORK_ERR_OK};
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
void Server::associate_cid(const ngtcp2_cid *cid, Handler *h) {
|
|
2995
|
+
handlers_.emplace(util::make_cid_key(cid), h);
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
void Server::dissociate_cid(const ngtcp2_cid *cid) {
|
|
2999
|
+
handlers_.erase(util::make_cid_key(cid));
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
void Server::remove(const Handler *h) {
|
|
3003
|
+
auto conn = h->conn();
|
|
3004
|
+
|
|
3005
|
+
dissociate_cid(ngtcp2_conn_get_client_initial_dcid(conn));
|
|
3006
|
+
|
|
3007
|
+
std::vector<ngtcp2_cid> cids(ngtcp2_conn_get_num_scid(conn));
|
|
3008
|
+
ngtcp2_conn_get_scid(conn, cids.data());
|
|
3009
|
+
|
|
3010
|
+
for (auto &cid : cids) {
|
|
3011
|
+
dissociate_cid(&cid);
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
delete h;
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
namespace {
|
|
3018
|
+
int parse_host_port(Address &dest, int af, const char *first,
|
|
3019
|
+
const char *last) {
|
|
3020
|
+
if (std::distance(first, last) == 0) {
|
|
3021
|
+
return -1;
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
const char *host_begin, *host_end, *it;
|
|
3025
|
+
if (*first == '[') {
|
|
3026
|
+
host_begin = first + 1;
|
|
3027
|
+
it = std::find(host_begin, last, ']');
|
|
3028
|
+
if (it == last) {
|
|
3029
|
+
return -1;
|
|
3030
|
+
}
|
|
3031
|
+
host_end = it;
|
|
3032
|
+
++it;
|
|
3033
|
+
if (it == last || *it != ':') {
|
|
3034
|
+
return -1;
|
|
3035
|
+
}
|
|
3036
|
+
} else {
|
|
3037
|
+
host_begin = first;
|
|
3038
|
+
it = std::find(host_begin, last, ':');
|
|
3039
|
+
if (it == last) {
|
|
3040
|
+
return -1;
|
|
3041
|
+
}
|
|
3042
|
+
host_end = it;
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
if (++it == last) {
|
|
3046
|
+
return -1;
|
|
3047
|
+
}
|
|
3048
|
+
auto svc_begin = it;
|
|
3049
|
+
|
|
3050
|
+
std::array<char, NI_MAXHOST> host;
|
|
3051
|
+
*std::copy(host_begin, host_end, std::begin(host)) = '\0';
|
|
3052
|
+
|
|
3053
|
+
addrinfo hints{}, *res;
|
|
3054
|
+
hints.ai_family = af;
|
|
3055
|
+
hints.ai_socktype = SOCK_DGRAM;
|
|
3056
|
+
|
|
3057
|
+
if (auto rv = getaddrinfo(host.data(), svc_begin, &hints, &res); rv != 0) {
|
|
3058
|
+
std::cerr << "getaddrinfo: [" << host.data() << "]:" << svc_begin << ": "
|
|
3059
|
+
<< gai_strerror(rv) << std::endl;
|
|
3060
|
+
return -1;
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
dest.len = res->ai_addrlen;
|
|
3064
|
+
memcpy(&dest.su, res->ai_addr, res->ai_addrlen);
|
|
3065
|
+
|
|
3066
|
+
freeaddrinfo(res);
|
|
3067
|
+
|
|
3068
|
+
return 0;
|
|
3069
|
+
}
|
|
3070
|
+
} // namespace
|
|
3071
|
+
|
|
3072
|
+
namespace {
|
|
3073
|
+
void print_usage() {
|
|
3074
|
+
std::cerr << "Usage: server [OPTIONS] <ADDR> <PORT> <PRIVATE_KEY_FILE> "
|
|
3075
|
+
"<CERTIFICATE_FILE>"
|
|
3076
|
+
<< std::endl;
|
|
3077
|
+
}
|
|
3078
|
+
} // namespace
|
|
3079
|
+
|
|
3080
|
+
namespace {
|
|
3081
|
+
void config_set_default(Config &config) {
|
|
3082
|
+
config = Config{};
|
|
3083
|
+
config.tx_loss_prob = 0.;
|
|
3084
|
+
config.rx_loss_prob = 0.;
|
|
3085
|
+
config.ciphers = util::crypto_default_ciphers();
|
|
3086
|
+
config.groups = util::crypto_default_groups();
|
|
3087
|
+
config.timeout = 30 * NGTCP2_SECONDS;
|
|
3088
|
+
{
|
|
3089
|
+
auto path = realpath(".", nullptr);
|
|
3090
|
+
assert(path);
|
|
3091
|
+
config.htdocs = path;
|
|
3092
|
+
free(path);
|
|
3093
|
+
}
|
|
3094
|
+
config.mime_types_file = "/etc/mime.types"sv;
|
|
3095
|
+
config.max_data = 1_m;
|
|
3096
|
+
config.max_stream_data_bidi_local = 256_k;
|
|
3097
|
+
config.max_stream_data_bidi_remote = 256_k;
|
|
3098
|
+
config.max_stream_data_uni = 256_k;
|
|
3099
|
+
config.max_window = 6_m;
|
|
3100
|
+
config.max_stream_window = 6_m;
|
|
3101
|
+
config.max_streams_bidi = 100;
|
|
3102
|
+
config.max_streams_uni = 3;
|
|
3103
|
+
config.max_dyn_length = 20_m;
|
|
3104
|
+
config.cc_algo = NGTCP2_CC_ALGO_CUBIC;
|
|
3105
|
+
config.initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT;
|
|
3106
|
+
config.max_gso_dgrams = 64;
|
|
3107
|
+
config.handshake_timeout = UINT64_MAX;
|
|
3108
|
+
config.ack_thresh = 2;
|
|
3109
|
+
}
|
|
3110
|
+
} // namespace
|
|
3111
|
+
|
|
3112
|
+
namespace {
|
|
3113
|
+
void print_help() {
|
|
3114
|
+
print_usage();
|
|
3115
|
+
|
|
3116
|
+
config_set_default(config);
|
|
3117
|
+
|
|
3118
|
+
std::cout << R"(
|
|
3119
|
+
<ADDR> Address to listen to. '*' binds to any address.
|
|
3120
|
+
<PORT> Port
|
|
3121
|
+
<PRIVATE_KEY_FILE>
|
|
3122
|
+
Path to private key file
|
|
3123
|
+
<CERTIFICATE_FILE>
|
|
3124
|
+
Path to certificate file
|
|
3125
|
+
Options:
|
|
3126
|
+
-t, --tx-loss=<P>
|
|
3127
|
+
The probability of losing outgoing packets. <P> must be
|
|
3128
|
+
[0.0, 1.0], inclusive. 0.0 means no packet loss. 1.0
|
|
3129
|
+
means 100% packet loss.
|
|
3130
|
+
-r, --rx-loss=<P>
|
|
3131
|
+
The probability of losing incoming packets. <P> must be
|
|
3132
|
+
[0.0, 1.0], inclusive. 0.0 means no packet loss. 1.0
|
|
3133
|
+
means 100% packet loss.
|
|
3134
|
+
--ciphers=<CIPHERS>
|
|
3135
|
+
Specify the cipher suite list to enable.
|
|
3136
|
+
Default: )"
|
|
3137
|
+
<< config.ciphers << R"(
|
|
3138
|
+
--groups=<GROUPS>
|
|
3139
|
+
Specify the supported groups.
|
|
3140
|
+
Default: )"
|
|
3141
|
+
<< config.groups << R"(
|
|
3142
|
+
-d, --htdocs=<PATH>
|
|
3143
|
+
Specify document root. If this option is not specified,
|
|
3144
|
+
the document root is the current working directory.
|
|
3145
|
+
-q, --quiet Suppress debug output.
|
|
3146
|
+
-s, --show-secret
|
|
3147
|
+
Print out secrets unless --quiet is used.
|
|
3148
|
+
--timeout=<DURATION>
|
|
3149
|
+
Specify idle timeout.
|
|
3150
|
+
Default: )"
|
|
3151
|
+
<< util::format_duration(config.timeout) << R"(
|
|
3152
|
+
-V, --validate-addr
|
|
3153
|
+
Perform address validation.
|
|
3154
|
+
--preferred-ipv4-addr=<ADDR>:<PORT>
|
|
3155
|
+
Specify preferred IPv4 address and port.
|
|
3156
|
+
--preferred-ipv6-addr=<ADDR>:<PORT>
|
|
3157
|
+
Specify preferred IPv6 address and port. A numeric IPv6
|
|
3158
|
+
address must be enclosed by '[' and ']' (e.g.,
|
|
3159
|
+
[::1]:8443)
|
|
3160
|
+
--mime-types-file=<PATH>
|
|
3161
|
+
Path to file that contains MIME media types and the
|
|
3162
|
+
extensions.
|
|
3163
|
+
Default: )"
|
|
3164
|
+
<< config.mime_types_file << R"(
|
|
3165
|
+
--early-response
|
|
3166
|
+
Start sending response when it receives HTTP header
|
|
3167
|
+
fields without waiting for request body. If HTTP
|
|
3168
|
+
response data is written before receiving request body,
|
|
3169
|
+
STOP_SENDING is sent.
|
|
3170
|
+
--verify-client
|
|
3171
|
+
Request a client certificate. At the moment, we just
|
|
3172
|
+
request a certificate and no verification is done.
|
|
3173
|
+
--qlog-dir=<PATH>
|
|
3174
|
+
Path to the directory where qlog file is stored. The
|
|
3175
|
+
file name of each qlog is the Source Connection ID of
|
|
3176
|
+
server.
|
|
3177
|
+
--no-quic-dump
|
|
3178
|
+
Disables printing QUIC STREAM and CRYPTO frame data out.
|
|
3179
|
+
--no-http-dump
|
|
3180
|
+
Disables printing HTTP response body out.
|
|
3181
|
+
--max-data=<SIZE>
|
|
3182
|
+
The initial connection-level flow control window.
|
|
3183
|
+
Default: )"
|
|
3184
|
+
<< util::format_uint_iec(config.max_data) << R"(
|
|
3185
|
+
--max-stream-data-bidi-local=<SIZE>
|
|
3186
|
+
The initial stream-level flow control window for a
|
|
3187
|
+
bidirectional stream that the local endpoint initiates.
|
|
3188
|
+
Default: )"
|
|
3189
|
+
<< util::format_uint_iec(config.max_stream_data_bidi_local) << R"(
|
|
3190
|
+
--max-stream-data-bidi-remote=<SIZE>
|
|
3191
|
+
The initial stream-level flow control window for a
|
|
3192
|
+
bidirectional stream that the remote endpoint initiates.
|
|
3193
|
+
Default: )"
|
|
3194
|
+
<< util::format_uint_iec(config.max_stream_data_bidi_remote) << R"(
|
|
3195
|
+
--max-stream-data-uni=<SIZE>
|
|
3196
|
+
The initial stream-level flow control window for a
|
|
3197
|
+
unidirectional stream.
|
|
3198
|
+
Default: )"
|
|
3199
|
+
<< util::format_uint_iec(config.max_stream_data_uni) << R"(
|
|
3200
|
+
--max-streams-bidi=<N>
|
|
3201
|
+
The number of the concurrent bidirectional streams.
|
|
3202
|
+
Default: )"
|
|
3203
|
+
<< config.max_streams_bidi << R"(
|
|
3204
|
+
--max-streams-uni=<N>
|
|
3205
|
+
The number of the concurrent unidirectional streams.
|
|
3206
|
+
Default: )"
|
|
3207
|
+
<< config.max_streams_uni << R"(
|
|
3208
|
+
--max-dyn-length=<SIZE>
|
|
3209
|
+
The maximum length of a dynamically generated content.
|
|
3210
|
+
Default: )"
|
|
3211
|
+
<< util::format_uint_iec(config.max_dyn_length) << R"(
|
|
3212
|
+
--cc=(cubic|reno|bbr|bbr2)
|
|
3213
|
+
The name of congestion controller algorithm.
|
|
3214
|
+
Default: )"
|
|
3215
|
+
<< util::strccalgo(config.cc_algo) << R"(
|
|
3216
|
+
--initial-rtt=<DURATION>
|
|
3217
|
+
Set an initial RTT.
|
|
3218
|
+
Default: )"
|
|
3219
|
+
<< util::format_duration(config.initial_rtt) << R"(
|
|
3220
|
+
--max-udp-payload-size=<SIZE>
|
|
3221
|
+
Override maximum UDP payload size that server transmits.
|
|
3222
|
+
--send-trailers
|
|
3223
|
+
Send trailer fields.
|
|
3224
|
+
--max-window=<SIZE>
|
|
3225
|
+
Maximum connection-level flow control window size. The
|
|
3226
|
+
window auto-tuning is enabled if nonzero value is given,
|
|
3227
|
+
and window size is scaled up to this value.
|
|
3228
|
+
Default: )"
|
|
3229
|
+
<< util::format_uint_iec(config.max_window) << R"(
|
|
3230
|
+
--max-stream-window=<SIZE>
|
|
3231
|
+
Maximum stream-level flow control window size. The
|
|
3232
|
+
window auto-tuning is enabled if nonzero value is given,
|
|
3233
|
+
and window size is scaled up to this value.
|
|
3234
|
+
Default: )"
|
|
3235
|
+
<< util::format_uint_iec(config.max_stream_window) << R"(
|
|
3236
|
+
--max-gso-dgrams=<N>
|
|
3237
|
+
Maximum number of UDP datagrams that are sent in a
|
|
3238
|
+
single GSO sendmsg call.
|
|
3239
|
+
Default: )"
|
|
3240
|
+
<< config.max_gso_dgrams << R"(
|
|
3241
|
+
--handshake-timeout=<DURATION>
|
|
3242
|
+
Set the QUIC handshake timeout. It defaults to no
|
|
3243
|
+
timeout.
|
|
3244
|
+
--preferred-versions=<HEX>[[,<HEX>]...]
|
|
3245
|
+
Specify QUIC versions in hex string in the order of
|
|
3246
|
+
preference. Server negotiates one of those versions if
|
|
3247
|
+
client initially selects a less preferred version.
|
|
3248
|
+
These versions must be supported by libngtcp2. Instead
|
|
3249
|
+
of specifying hex string, there are special aliases
|
|
3250
|
+
available: "v1" indicates QUIC v1, and "v2" indicates
|
|
3251
|
+
QUIC v2.
|
|
3252
|
+
--available-versions=<HEX>[[,<HEX>]...]
|
|
3253
|
+
Specify QUIC versions in hex string that are sent in
|
|
3254
|
+
available_versions field of version_information
|
|
3255
|
+
transport parameter. This list can include a version
|
|
3256
|
+
which is not supported by libngtcp2. Instead of
|
|
3257
|
+
specifying hex string, there are special aliases
|
|
3258
|
+
available: "v1" indicates QUIC v1, and "v2" indicates
|
|
3259
|
+
QUIC v2.
|
|
3260
|
+
--no-pmtud Disables Path MTU Discovery.
|
|
3261
|
+
--ack-thresh=<N>
|
|
3262
|
+
The minimum number of the received ACK eliciting packets
|
|
3263
|
+
that triggers immediate acknowledgement.
|
|
3264
|
+
Default: )"
|
|
3265
|
+
<< config.ack_thresh << R"(
|
|
3266
|
+
-h, --help Display this help and exit.
|
|
3267
|
+
|
|
3268
|
+
---
|
|
3269
|
+
|
|
3270
|
+
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
|
3271
|
+
10 * 1024). Units are K, M and G (powers of 1024).
|
|
3272
|
+
|
|
3273
|
+
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
|
3274
|
+
is 1 second and 500ms is 500 milliseconds). Units are h, m, s, ms,
|
|
3275
|
+
us, or ns (hours, minutes, seconds, milliseconds, microseconds, and
|
|
3276
|
+
nanoseconds respectively). If a unit is omitted, a second is used
|
|
3277
|
+
as unit.
|
|
3278
|
+
|
|
3279
|
+
The <HEX> argument is an hex string which must start with "0x"
|
|
3280
|
+
(e.g., 0x00000001).)"
|
|
3281
|
+
<< std::endl;
|
|
3282
|
+
}
|
|
3283
|
+
} // namespace
|
|
3284
|
+
|
|
3285
|
+
std::ofstream keylog_file;
|
|
3286
|
+
|
|
3287
|
+
int main(int argc, char **argv) {
|
|
3288
|
+
config_set_default(config);
|
|
3289
|
+
|
|
3290
|
+
for (;;) {
|
|
3291
|
+
static int flag = 0;
|
|
3292
|
+
constexpr static option long_opts[] = {
|
|
3293
|
+
{"help", no_argument, nullptr, 'h'},
|
|
3294
|
+
{"tx-loss", required_argument, nullptr, 't'},
|
|
3295
|
+
{"rx-loss", required_argument, nullptr, 'r'},
|
|
3296
|
+
{"htdocs", required_argument, nullptr, 'd'},
|
|
3297
|
+
{"quiet", no_argument, nullptr, 'q'},
|
|
3298
|
+
{"show-secret", no_argument, nullptr, 's'},
|
|
3299
|
+
{"validate-addr", no_argument, nullptr, 'V'},
|
|
3300
|
+
{"ciphers", required_argument, &flag, 1},
|
|
3301
|
+
{"groups", required_argument, &flag, 2},
|
|
3302
|
+
{"timeout", required_argument, &flag, 3},
|
|
3303
|
+
{"preferred-ipv4-addr", required_argument, &flag, 4},
|
|
3304
|
+
{"preferred-ipv6-addr", required_argument, &flag, 5},
|
|
3305
|
+
{"mime-types-file", required_argument, &flag, 6},
|
|
3306
|
+
{"early-response", no_argument, &flag, 7},
|
|
3307
|
+
{"verify-client", no_argument, &flag, 8},
|
|
3308
|
+
{"qlog-dir", required_argument, &flag, 9},
|
|
3309
|
+
{"no-quic-dump", no_argument, &flag, 10},
|
|
3310
|
+
{"no-http-dump", no_argument, &flag, 11},
|
|
3311
|
+
{"max-data", required_argument, &flag, 12},
|
|
3312
|
+
{"max-stream-data-bidi-local", required_argument, &flag, 13},
|
|
3313
|
+
{"max-stream-data-bidi-remote", required_argument, &flag, 14},
|
|
3314
|
+
{"max-stream-data-uni", required_argument, &flag, 15},
|
|
3315
|
+
{"max-streams-bidi", required_argument, &flag, 16},
|
|
3316
|
+
{"max-streams-uni", required_argument, &flag, 17},
|
|
3317
|
+
{"max-dyn-length", required_argument, &flag, 18},
|
|
3318
|
+
{"cc", required_argument, &flag, 19},
|
|
3319
|
+
{"initial-rtt", required_argument, &flag, 20},
|
|
3320
|
+
{"max-udp-payload-size", required_argument, &flag, 21},
|
|
3321
|
+
{"send-trailers", no_argument, &flag, 22},
|
|
3322
|
+
{"max-window", required_argument, &flag, 23},
|
|
3323
|
+
{"max-stream-window", required_argument, &flag, 24},
|
|
3324
|
+
{"max-gso-dgrams", required_argument, &flag, 25},
|
|
3325
|
+
{"handshake-timeout", required_argument, &flag, 26},
|
|
3326
|
+
{"preferred-versions", required_argument, &flag, 27},
|
|
3327
|
+
{"available-versions", required_argument, &flag, 28},
|
|
3328
|
+
{"no-pmtud", no_argument, &flag, 29},
|
|
3329
|
+
{"ack-thresh", required_argument, &flag, 30},
|
|
3330
|
+
{nullptr, 0, nullptr, 0}};
|
|
3331
|
+
|
|
3332
|
+
auto optidx = 0;
|
|
3333
|
+
auto c = getopt_long(argc, argv, "d:hqr:st:V", long_opts, &optidx);
|
|
3334
|
+
if (c == -1) {
|
|
3335
|
+
break;
|
|
3336
|
+
}
|
|
3337
|
+
switch (c) {
|
|
3338
|
+
case 'd': {
|
|
3339
|
+
// --htdocs
|
|
3340
|
+
auto path = realpath(optarg, nullptr);
|
|
3341
|
+
if (path == nullptr) {
|
|
3342
|
+
std::cerr << "path: invalid path " << std::quoted(optarg) << std::endl;
|
|
3343
|
+
exit(EXIT_FAILURE);
|
|
3344
|
+
}
|
|
3345
|
+
config.htdocs = path;
|
|
3346
|
+
free(path);
|
|
3347
|
+
break;
|
|
3348
|
+
}
|
|
3349
|
+
case 'h':
|
|
3350
|
+
// --help
|
|
3351
|
+
print_help();
|
|
3352
|
+
exit(EXIT_SUCCESS);
|
|
3353
|
+
case 'q':
|
|
3354
|
+
// --quiet
|
|
3355
|
+
config.quiet = true;
|
|
3356
|
+
break;
|
|
3357
|
+
case 'r':
|
|
3358
|
+
// --rx-loss
|
|
3359
|
+
config.rx_loss_prob = strtod(optarg, nullptr);
|
|
3360
|
+
break;
|
|
3361
|
+
case 's':
|
|
3362
|
+
// --show-secret
|
|
3363
|
+
config.show_secret = true;
|
|
3364
|
+
break;
|
|
3365
|
+
case 't':
|
|
3366
|
+
// --tx-loss
|
|
3367
|
+
config.tx_loss_prob = strtod(optarg, nullptr);
|
|
3368
|
+
break;
|
|
3369
|
+
case 'V':
|
|
3370
|
+
// --validate-addr
|
|
3371
|
+
config.validate_addr = true;
|
|
3372
|
+
break;
|
|
3373
|
+
case '?':
|
|
3374
|
+
print_usage();
|
|
3375
|
+
exit(EXIT_FAILURE);
|
|
3376
|
+
case 0:
|
|
3377
|
+
switch (flag) {
|
|
3378
|
+
case 1:
|
|
3379
|
+
// --ciphers
|
|
3380
|
+
config.ciphers = optarg;
|
|
3381
|
+
break;
|
|
3382
|
+
case 2:
|
|
3383
|
+
// --groups
|
|
3384
|
+
config.groups = optarg;
|
|
3385
|
+
break;
|
|
3386
|
+
case 3:
|
|
3387
|
+
// --timeout
|
|
3388
|
+
if (auto t = util::parse_duration(optarg); !t) {
|
|
3389
|
+
std::cerr << "timeout: invalid argument" << std::endl;
|
|
3390
|
+
exit(EXIT_FAILURE);
|
|
3391
|
+
} else {
|
|
3392
|
+
config.timeout = *t;
|
|
3393
|
+
}
|
|
3394
|
+
break;
|
|
3395
|
+
case 4:
|
|
3396
|
+
// --preferred-ipv4-addr
|
|
3397
|
+
if (parse_host_port(config.preferred_ipv4_addr, AF_INET, optarg,
|
|
3398
|
+
optarg + strlen(optarg)) != 0) {
|
|
3399
|
+
std::cerr << "preferred-ipv4-addr: could not use "
|
|
3400
|
+
<< std::quoted(optarg) << std::endl;
|
|
3401
|
+
exit(EXIT_FAILURE);
|
|
3402
|
+
}
|
|
3403
|
+
break;
|
|
3404
|
+
case 5:
|
|
3405
|
+
// --preferred-ipv6-addr
|
|
3406
|
+
if (parse_host_port(config.preferred_ipv6_addr, AF_INET6, optarg,
|
|
3407
|
+
optarg + strlen(optarg)) != 0) {
|
|
3408
|
+
std::cerr << "preferred-ipv6-addr: could not use "
|
|
3409
|
+
<< std::quoted(optarg) << std::endl;
|
|
3410
|
+
exit(EXIT_FAILURE);
|
|
3411
|
+
}
|
|
3412
|
+
break;
|
|
3413
|
+
case 6:
|
|
3414
|
+
// --mime-types-file
|
|
3415
|
+
config.mime_types_file = optarg;
|
|
3416
|
+
break;
|
|
3417
|
+
case 7:
|
|
3418
|
+
// --early-response
|
|
3419
|
+
config.early_response = true;
|
|
3420
|
+
break;
|
|
3421
|
+
case 8:
|
|
3422
|
+
// --verify-client
|
|
3423
|
+
config.verify_client = true;
|
|
3424
|
+
break;
|
|
3425
|
+
case 9:
|
|
3426
|
+
// --qlog-dir
|
|
3427
|
+
config.qlog_dir = optarg;
|
|
3428
|
+
break;
|
|
3429
|
+
case 10:
|
|
3430
|
+
// --no-quic-dump
|
|
3431
|
+
config.no_quic_dump = true;
|
|
3432
|
+
break;
|
|
3433
|
+
case 11:
|
|
3434
|
+
// --no-http-dump
|
|
3435
|
+
config.no_http_dump = true;
|
|
3436
|
+
break;
|
|
3437
|
+
case 12:
|
|
3438
|
+
// --max-data
|
|
3439
|
+
if (auto n = util::parse_uint_iec(optarg); !n) {
|
|
3440
|
+
std::cerr << "max-data: invalid argument" << std::endl;
|
|
3441
|
+
exit(EXIT_FAILURE);
|
|
3442
|
+
} else {
|
|
3443
|
+
config.max_data = *n;
|
|
3444
|
+
}
|
|
3445
|
+
break;
|
|
3446
|
+
case 13:
|
|
3447
|
+
// --max-stream-data-bidi-local
|
|
3448
|
+
if (auto n = util::parse_uint_iec(optarg); !n) {
|
|
3449
|
+
std::cerr << "max-stream-data-bidi-local: invalid argument"
|
|
3450
|
+
<< std::endl;
|
|
3451
|
+
exit(EXIT_FAILURE);
|
|
3452
|
+
} else {
|
|
3453
|
+
config.max_stream_data_bidi_local = *n;
|
|
3454
|
+
}
|
|
3455
|
+
break;
|
|
3456
|
+
case 14:
|
|
3457
|
+
// --max-stream-data-bidi-remote
|
|
3458
|
+
if (auto n = util::parse_uint_iec(optarg); !n) {
|
|
3459
|
+
std::cerr << "max-stream-data-bidi-remote: invalid argument"
|
|
3460
|
+
<< std::endl;
|
|
3461
|
+
exit(EXIT_FAILURE);
|
|
3462
|
+
} else {
|
|
3463
|
+
config.max_stream_data_bidi_remote = *n;
|
|
3464
|
+
}
|
|
3465
|
+
break;
|
|
3466
|
+
case 15:
|
|
3467
|
+
// --max-stream-data-uni
|
|
3468
|
+
if (auto n = util::parse_uint_iec(optarg); !n) {
|
|
3469
|
+
std::cerr << "max-stream-data-uni: invalid argument" << std::endl;
|
|
3470
|
+
exit(EXIT_FAILURE);
|
|
3471
|
+
} else {
|
|
3472
|
+
config.max_stream_data_uni = *n;
|
|
3473
|
+
}
|
|
3474
|
+
break;
|
|
3475
|
+
case 16:
|
|
3476
|
+
// --max-streams-bidi
|
|
3477
|
+
if (auto n = util::parse_uint(optarg); !n) {
|
|
3478
|
+
std::cerr << "max-streams-bidi: invalid argument" << std::endl;
|
|
3479
|
+
exit(EXIT_FAILURE);
|
|
3480
|
+
} else {
|
|
3481
|
+
config.max_streams_bidi = *n;
|
|
3482
|
+
}
|
|
3483
|
+
break;
|
|
3484
|
+
case 17:
|
|
3485
|
+
// --max-streams-uni
|
|
3486
|
+
if (auto n = util::parse_uint(optarg); !n) {
|
|
3487
|
+
std::cerr << "max-streams-uni: invalid argument" << std::endl;
|
|
3488
|
+
exit(EXIT_FAILURE);
|
|
3489
|
+
} else {
|
|
3490
|
+
config.max_streams_uni = *n;
|
|
3491
|
+
}
|
|
3492
|
+
break;
|
|
3493
|
+
case 18:
|
|
3494
|
+
// --max-dyn-length
|
|
3495
|
+
if (auto n = util::parse_uint_iec(optarg); !n) {
|
|
3496
|
+
std::cerr << "max-dyn-length: invalid argument" << std::endl;
|
|
3497
|
+
exit(EXIT_FAILURE);
|
|
3498
|
+
} else {
|
|
3499
|
+
config.max_dyn_length = *n;
|
|
3500
|
+
}
|
|
3501
|
+
break;
|
|
3502
|
+
case 19:
|
|
3503
|
+
// --cc
|
|
3504
|
+
if (strcmp("cubic", optarg) == 0) {
|
|
3505
|
+
config.cc_algo = NGTCP2_CC_ALGO_CUBIC;
|
|
3506
|
+
break;
|
|
3507
|
+
}
|
|
3508
|
+
if (strcmp("reno", optarg) == 0) {
|
|
3509
|
+
config.cc_algo = NGTCP2_CC_ALGO_RENO;
|
|
3510
|
+
break;
|
|
3511
|
+
}
|
|
3512
|
+
if (strcmp("bbr", optarg) == 0) {
|
|
3513
|
+
config.cc_algo = NGTCP2_CC_ALGO_BBR;
|
|
3514
|
+
break;
|
|
3515
|
+
}
|
|
3516
|
+
if (strcmp("bbr2", optarg) == 0) {
|
|
3517
|
+
config.cc_algo = NGTCP2_CC_ALGO_BBR2;
|
|
3518
|
+
break;
|
|
3519
|
+
}
|
|
3520
|
+
std::cerr << "cc: specify cubic, reno, bbr, or bbr2" << std::endl;
|
|
3521
|
+
exit(EXIT_FAILURE);
|
|
3522
|
+
case 20:
|
|
3523
|
+
// --initial-rtt
|
|
3524
|
+
if (auto t = util::parse_duration(optarg); !t) {
|
|
3525
|
+
std::cerr << "initial-rtt: invalid argument" << std::endl;
|
|
3526
|
+
exit(EXIT_FAILURE);
|
|
3527
|
+
} else {
|
|
3528
|
+
config.initial_rtt = *t;
|
|
3529
|
+
}
|
|
3530
|
+
break;
|
|
3531
|
+
case 21:
|
|
3532
|
+
// --max-udp-payload-size
|
|
3533
|
+
if (auto n = util::parse_uint_iec(optarg); !n) {
|
|
3534
|
+
std::cerr << "max-udp-payload-size: invalid argument" << std::endl;
|
|
3535
|
+
exit(EXIT_FAILURE);
|
|
3536
|
+
} else if (*n > 64_k) {
|
|
3537
|
+
std::cerr << "max-udp-payload-size: must not exceed 65536"
|
|
3538
|
+
<< std::endl;
|
|
3539
|
+
exit(EXIT_FAILURE);
|
|
3540
|
+
} else {
|
|
3541
|
+
config.max_udp_payload_size = *n;
|
|
3542
|
+
}
|
|
3543
|
+
break;
|
|
3544
|
+
case 22:
|
|
3545
|
+
// --send-trailers
|
|
3546
|
+
config.send_trailers = true;
|
|
3547
|
+
break;
|
|
3548
|
+
case 23:
|
|
3549
|
+
// --max-window
|
|
3550
|
+
if (auto n = util::parse_uint_iec(optarg); !n) {
|
|
3551
|
+
std::cerr << "max-window: invalid argument" << std::endl;
|
|
3552
|
+
exit(EXIT_FAILURE);
|
|
3553
|
+
} else {
|
|
3554
|
+
config.max_window = *n;
|
|
3555
|
+
}
|
|
3556
|
+
break;
|
|
3557
|
+
case 24:
|
|
3558
|
+
// --max-stream-window
|
|
3559
|
+
if (auto n = util::parse_uint_iec(optarg); !n) {
|
|
3560
|
+
std::cerr << "max-stream-window: invalid argument" << std::endl;
|
|
3561
|
+
exit(EXIT_FAILURE);
|
|
3562
|
+
} else {
|
|
3563
|
+
config.max_stream_window = *n;
|
|
3564
|
+
}
|
|
3565
|
+
break;
|
|
3566
|
+
case 25:
|
|
3567
|
+
// --max-gso-dgrams
|
|
3568
|
+
if (auto n = util::parse_uint(optarg); !n) {
|
|
3569
|
+
std::cerr << "max-gso-dgrams: invalid argument" << std::endl;
|
|
3570
|
+
exit(EXIT_FAILURE);
|
|
3571
|
+
} else {
|
|
3572
|
+
config.max_gso_dgrams = *n;
|
|
3573
|
+
}
|
|
3574
|
+
break;
|
|
3575
|
+
case 26:
|
|
3576
|
+
// --handshake-timeout
|
|
3577
|
+
if (auto t = util::parse_duration(optarg); !t) {
|
|
3578
|
+
std::cerr << "handshake-timeout: invalid argument" << std::endl;
|
|
3579
|
+
exit(EXIT_FAILURE);
|
|
3580
|
+
} else {
|
|
3581
|
+
config.handshake_timeout = *t;
|
|
3582
|
+
}
|
|
3583
|
+
break;
|
|
3584
|
+
case 27: {
|
|
3585
|
+
// --preferred-versions
|
|
3586
|
+
auto l = util::split_str(optarg);
|
|
3587
|
+
if (l.size() > max_preferred_versionslen) {
|
|
3588
|
+
std::cerr << "preferred-versions: too many versions > "
|
|
3589
|
+
<< max_preferred_versionslen << std::endl;
|
|
3590
|
+
}
|
|
3591
|
+
config.preferred_versions.resize(l.size());
|
|
3592
|
+
auto it = std::begin(config.preferred_versions);
|
|
3593
|
+
for (const auto &k : l) {
|
|
3594
|
+
if (k == "v1"sv) {
|
|
3595
|
+
*it++ = NGTCP2_PROTO_VER_V1;
|
|
3596
|
+
continue;
|
|
3597
|
+
}
|
|
3598
|
+
if (k == "v2"sv) {
|
|
3599
|
+
*it++ = NGTCP2_PROTO_VER_V2;
|
|
3600
|
+
continue;
|
|
3601
|
+
}
|
|
3602
|
+
auto rv = util::parse_version(k);
|
|
3603
|
+
if (!rv) {
|
|
3604
|
+
std::cerr << "preferred-versions: invalid version "
|
|
3605
|
+
<< std::quoted(k) << std::endl;
|
|
3606
|
+
exit(EXIT_FAILURE);
|
|
3607
|
+
}
|
|
3608
|
+
if (!ngtcp2_is_supported_version(*rv)) {
|
|
3609
|
+
std::cerr << "preferred-versions: unsupported version "
|
|
3610
|
+
<< std::quoted(k) << std::endl;
|
|
3611
|
+
exit(EXIT_FAILURE);
|
|
3612
|
+
}
|
|
3613
|
+
*it++ = *rv;
|
|
3614
|
+
}
|
|
3615
|
+
break;
|
|
3616
|
+
}
|
|
3617
|
+
case 28: {
|
|
3618
|
+
// --available-versions
|
|
3619
|
+
auto l = util::split_str(optarg);
|
|
3620
|
+
config.available_versions.resize(l.size());
|
|
3621
|
+
auto it = std::begin(config.available_versions);
|
|
3622
|
+
for (const auto &k : l) {
|
|
3623
|
+
if (k == "v1"sv) {
|
|
3624
|
+
*it++ = NGTCP2_PROTO_VER_V1;
|
|
3625
|
+
continue;
|
|
3626
|
+
}
|
|
3627
|
+
if (k == "v2"sv) {
|
|
3628
|
+
*it++ = NGTCP2_PROTO_VER_V2;
|
|
3629
|
+
continue;
|
|
3630
|
+
}
|
|
3631
|
+
auto rv = util::parse_version(k);
|
|
3632
|
+
if (!rv) {
|
|
3633
|
+
std::cerr << "available-versions: invalid version "
|
|
3634
|
+
<< std::quoted(k) << std::endl;
|
|
3635
|
+
exit(EXIT_FAILURE);
|
|
3636
|
+
}
|
|
3637
|
+
*it++ = *rv;
|
|
3638
|
+
}
|
|
3639
|
+
break;
|
|
3640
|
+
}
|
|
3641
|
+
case 29:
|
|
3642
|
+
// --no-pmtud
|
|
3643
|
+
config.no_pmtud = true;
|
|
3644
|
+
break;
|
|
3645
|
+
case 30:
|
|
3646
|
+
// --ack-thresh
|
|
3647
|
+
if (auto n = util::parse_uint(optarg); !n) {
|
|
3648
|
+
std::cerr << "ack-thresh: invalid argument" << std::endl;
|
|
3649
|
+
exit(EXIT_FAILURE);
|
|
3650
|
+
} else if (*n > 100) {
|
|
3651
|
+
std::cerr << "ack-thresh: must not exceed 100" << std::endl;
|
|
3652
|
+
exit(EXIT_FAILURE);
|
|
3653
|
+
} else {
|
|
3654
|
+
config.ack_thresh = *n;
|
|
3655
|
+
}
|
|
3656
|
+
break;
|
|
3657
|
+
}
|
|
3658
|
+
break;
|
|
3659
|
+
default:
|
|
3660
|
+
break;
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3664
|
+
if (argc - optind < 4) {
|
|
3665
|
+
std::cerr << "Too few arguments" << std::endl;
|
|
3666
|
+
print_usage();
|
|
3667
|
+
exit(EXIT_FAILURE);
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3670
|
+
auto addr = argv[optind++];
|
|
3671
|
+
auto port = argv[optind++];
|
|
3672
|
+
auto private_key_file = argv[optind++];
|
|
3673
|
+
auto cert_file = argv[optind++];
|
|
3674
|
+
|
|
3675
|
+
if (auto n = util::parse_uint(port); !n) {
|
|
3676
|
+
std::cerr << "port: invalid port number" << std::endl;
|
|
3677
|
+
exit(EXIT_FAILURE);
|
|
3678
|
+
} else if (*n > 65535) {
|
|
3679
|
+
std::cerr << "port: must not exceed 65535" << std::endl;
|
|
3680
|
+
exit(EXIT_FAILURE);
|
|
3681
|
+
} else {
|
|
3682
|
+
config.port = *n;
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
if (auto mt = util::read_mime_types(config.mime_types_file); !mt) {
|
|
3686
|
+
std::cerr << "mime-types-file: Could not read MIME media types file "
|
|
3687
|
+
<< std::quoted(config.mime_types_file) << std::endl;
|
|
3688
|
+
} else {
|
|
3689
|
+
config.mime_types = std::move(*mt);
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
TLSServerContext tls_ctx;
|
|
3693
|
+
|
|
3694
|
+
if (tls_ctx.init(private_key_file, cert_file, AppProtocol::H3) != 0) {
|
|
3695
|
+
exit(EXIT_FAILURE);
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3698
|
+
if (config.htdocs.back() != '/') {
|
|
3699
|
+
config.htdocs += '/';
|
|
3700
|
+
}
|
|
3701
|
+
|
|
3702
|
+
std::cerr << "Using document root " << config.htdocs << std::endl;
|
|
3703
|
+
|
|
3704
|
+
auto ev_loop_d = defer(ev_loop_destroy, EV_DEFAULT);
|
|
3705
|
+
|
|
3706
|
+
auto keylog_filename = getenv("SSLKEYLOGFILE");
|
|
3707
|
+
if (keylog_filename) {
|
|
3708
|
+
keylog_file.open(keylog_filename, std::ios_base::app);
|
|
3709
|
+
if (keylog_file) {
|
|
3710
|
+
tls_ctx.enable_keylog();
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
|
|
3714
|
+
if (util::generate_secret(config.static_secret.data(),
|
|
3715
|
+
config.static_secret.size()) != 0) {
|
|
3716
|
+
std::cerr << "Unable to generate static secret" << std::endl;
|
|
3717
|
+
exit(EXIT_FAILURE);
|
|
3718
|
+
}
|
|
3719
|
+
|
|
3720
|
+
Server s(EV_DEFAULT, tls_ctx);
|
|
3721
|
+
if (s.init(addr, port) != 0) {
|
|
3722
|
+
exit(EXIT_FAILURE);
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
ev_run(EV_DEFAULT, 0);
|
|
3726
|
+
|
|
3727
|
+
s.disconnect();
|
|
3728
|
+
s.close();
|
|
3729
|
+
|
|
3730
|
+
return EXIT_SUCCESS;
|
|
3731
|
+
}
|