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.
Files changed (343) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/ext/ngtcp2/AUTHORS +44 -0
  4. data/ext/ngtcp2/CMakeLists.txt +431 -0
  5. data/ext/ngtcp2/CMakeOptions.txt +17 -0
  6. data/ext/ngtcp2/COPYING +22 -0
  7. data/ext/ngtcp2/ChangeLog +0 -0
  8. data/ext/ngtcp2/Makefile.am +60 -0
  9. data/ext/ngtcp2/NEWS +0 -0
  10. data/ext/ngtcp2/README +1 -0
  11. data/ext/ngtcp2/README.rst +258 -0
  12. data/ext/ngtcp2/ci/build_boringssl.sh +10 -0
  13. data/ext/ngtcp2/ci/build_nghttp3.sh +9 -0
  14. data/ext/ngtcp2/ci/build_openssl1.sh +8 -0
  15. data/ext/ngtcp2/ci/build_openssl1_cross.sh +9 -0
  16. data/ext/ngtcp2/ci/build_openssl3.sh +8 -0
  17. data/ext/ngtcp2/ci/build_picotls.sh +26 -0
  18. data/ext/ngtcp2/ci/build_wolfssl.sh +9 -0
  19. data/ext/ngtcp2/ci/gen-certificate.sh +8 -0
  20. data/ext/ngtcp2/cmake/ExtractValidFlags.cmake +31 -0
  21. data/ext/ngtcp2/cmake/FindCUnit.cmake +40 -0
  22. data/ext/ngtcp2/cmake/FindJemalloc.cmake +40 -0
  23. data/ext/ngtcp2/cmake/FindLibev.cmake +38 -0
  24. data/ext/ngtcp2/cmake/FindLibnghttp3.cmake +41 -0
  25. data/ext/ngtcp2/cmake/Findwolfssl.cmake +41 -0
  26. data/ext/ngtcp2/cmake/Version.cmake +11 -0
  27. data/ext/ngtcp2/cmakeconfig.h.in +36 -0
  28. data/ext/ngtcp2/configure.ac +755 -0
  29. data/ext/ngtcp2/crypto/CMakeLists.txt +56 -0
  30. data/ext/ngtcp2/crypto/Makefile.am +49 -0
  31. data/ext/ngtcp2/crypto/boringssl/CMakeLists.txt +64 -0
  32. data/ext/ngtcp2/crypto/boringssl/Makefile.am +39 -0
  33. data/ext/ngtcp2/crypto/boringssl/boringssl.c +630 -0
  34. data/ext/ngtcp2/crypto/boringssl/libngtcp2_crypto_boringssl.pc.in +33 -0
  35. data/ext/ngtcp2/crypto/gnutls/CMakeLists.txt +86 -0
  36. data/ext/ngtcp2/crypto/gnutls/Makefile.am +43 -0
  37. data/ext/ngtcp2/crypto/gnutls/gnutls.c +644 -0
  38. data/ext/ngtcp2/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in +33 -0
  39. data/ext/ngtcp2/crypto/includes/CMakeLists.txt +56 -0
  40. data/ext/ngtcp2/crypto/includes/Makefile.am +45 -0
  41. data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h +893 -0
  42. data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h +104 -0
  43. data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h +107 -0
  44. data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h +132 -0
  45. data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h +246 -0
  46. data/ext/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h +106 -0
  47. data/ext/ngtcp2/crypto/openssl/CMakeLists.txt +86 -0
  48. data/ext/ngtcp2/crypto/openssl/Makefile.am +43 -0
  49. data/ext/ngtcp2/crypto/openssl/libngtcp2_crypto_openssl.pc.in +33 -0
  50. data/ext/ngtcp2/crypto/openssl/openssl.c +807 -0
  51. data/ext/ngtcp2/crypto/picotls/CMakeLists.txt +65 -0
  52. data/ext/ngtcp2/crypto/picotls/Makefile.am +39 -0
  53. data/ext/ngtcp2/crypto/picotls/libngtcp2_crypto_picotls.pc.in +33 -0
  54. data/ext/ngtcp2/crypto/picotls/picotls.c +707 -0
  55. data/ext/ngtcp2/crypto/shared.c +1431 -0
  56. data/ext/ngtcp2/crypto/shared.h +350 -0
  57. data/ext/ngtcp2/crypto/wolfssl/CMakeLists.txt +84 -0
  58. data/ext/ngtcp2/crypto/wolfssl/Makefile.am +43 -0
  59. data/ext/ngtcp2/crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in +33 -0
  60. data/ext/ngtcp2/crypto/wolfssl/wolfssl.c +534 -0
  61. data/ext/ngtcp2/doc/Makefile.am +65 -0
  62. data/ext/ngtcp2/doc/make.bat +35 -0
  63. data/ext/ngtcp2/doc/mkapiref.py +356 -0
  64. data/ext/ngtcp2/doc/source/conf.py.in +94 -0
  65. data/ext/ngtcp2/doc/source/index.rst +22 -0
  66. data/ext/ngtcp2/doc/source/programmers-guide.rst +476 -0
  67. data/ext/ngtcp2/docker/Dockerfile +39 -0
  68. data/ext/ngtcp2/examples/CMakeLists.txt +361 -0
  69. data/ext/ngtcp2/examples/Makefile.am +228 -0
  70. data/ext/ngtcp2/examples/client.cc +3049 -0
  71. data/ext/ngtcp2/examples/client.h +192 -0
  72. data/ext/ngtcp2/examples/client_base.cc +202 -0
  73. data/ext/ngtcp2/examples/client_base.h +213 -0
  74. data/ext/ngtcp2/examples/debug.cc +298 -0
  75. data/ext/ngtcp2/examples/debug.h +124 -0
  76. data/ext/ngtcp2/examples/examplestest.cc +84 -0
  77. data/ext/ngtcp2/examples/gtlssimpleclient.c +720 -0
  78. data/ext/ngtcp2/examples/h09client.cc +2601 -0
  79. data/ext/ngtcp2/examples/h09client.h +196 -0
  80. data/ext/ngtcp2/examples/h09server.cc +3024 -0
  81. data/ext/ngtcp2/examples/h09server.h +237 -0
  82. data/ext/ngtcp2/examples/http.cc +138 -0
  83. data/ext/ngtcp2/examples/http.h +44 -0
  84. data/ext/ngtcp2/examples/network.h +80 -0
  85. data/ext/ngtcp2/examples/server.cc +3731 -0
  86. data/ext/ngtcp2/examples/server.h +256 -0
  87. data/ext/ngtcp2/examples/server_base.cc +58 -0
  88. data/ext/ngtcp2/examples/server_base.h +195 -0
  89. data/ext/ngtcp2/examples/shared.cc +385 -0
  90. data/ext/ngtcp2/examples/shared.h +96 -0
  91. data/ext/ngtcp2/examples/simpleclient.c +683 -0
  92. data/ext/ngtcp2/examples/template.h +71 -0
  93. data/ext/ngtcp2/examples/tests/README.rst +60 -0
  94. data/ext/ngtcp2/examples/tests/__init__.py +0 -0
  95. data/ext/ngtcp2/examples/tests/config.ini.in +32 -0
  96. data/ext/ngtcp2/examples/tests/conftest.py +28 -0
  97. data/ext/ngtcp2/examples/tests/ngtcp2test/__init__.py +6 -0
  98. data/ext/ngtcp2/examples/tests/ngtcp2test/certs.py +476 -0
  99. data/ext/ngtcp2/examples/tests/ngtcp2test/client.py +187 -0
  100. data/ext/ngtcp2/examples/tests/ngtcp2test/env.py +191 -0
  101. data/ext/ngtcp2/examples/tests/ngtcp2test/log.py +101 -0
  102. data/ext/ngtcp2/examples/tests/ngtcp2test/server.py +137 -0
  103. data/ext/ngtcp2/examples/tests/ngtcp2test/tls.py +983 -0
  104. data/ext/ngtcp2/examples/tests/test_01_handshake.py +30 -0
  105. data/ext/ngtcp2/examples/tests/test_02_resume.py +46 -0
  106. data/ext/ngtcp2/examples/tests/test_03_earlydata.py +56 -0
  107. data/ext/ngtcp2/examples/tests/test_04_clientcert.py +57 -0
  108. data/ext/ngtcp2/examples/tests/test_05_ciphers.py +46 -0
  109. data/ext/ngtcp2/examples/tls_client_context.h +52 -0
  110. data/ext/ngtcp2/examples/tls_client_context_boringssl.cc +126 -0
  111. data/ext/ngtcp2/examples/tls_client_context_boringssl.h +49 -0
  112. data/ext/ngtcp2/examples/tls_client_context_gnutls.cc +74 -0
  113. data/ext/ngtcp2/examples/tls_client_context_gnutls.h +50 -0
  114. data/ext/ngtcp2/examples/tls_client_context_openssl.cc +137 -0
  115. data/ext/ngtcp2/examples/tls_client_context_openssl.h +49 -0
  116. data/ext/ngtcp2/examples/tls_client_context_picotls.cc +158 -0
  117. data/ext/ngtcp2/examples/tls_client_context_picotls.h +53 -0
  118. data/ext/ngtcp2/examples/tls_client_context_wolfssl.cc +177 -0
  119. data/ext/ngtcp2/examples/tls_client_context_wolfssl.h +51 -0
  120. data/ext/ngtcp2/examples/tls_client_session.h +52 -0
  121. data/ext/ngtcp2/examples/tls_client_session_boringssl.cc +110 -0
  122. data/ext/ngtcp2/examples/tls_client_session_boringssl.h +52 -0
  123. data/ext/ngtcp2/examples/tls_client_session_gnutls.cc +190 -0
  124. data/ext/ngtcp2/examples/tls_client_session_gnutls.h +52 -0
  125. data/ext/ngtcp2/examples/tls_client_session_openssl.cc +113 -0
  126. data/ext/ngtcp2/examples/tls_client_session_openssl.h +52 -0
  127. data/ext/ngtcp2/examples/tls_client_session_picotls.cc +147 -0
  128. data/ext/ngtcp2/examples/tls_client_session_picotls.h +52 -0
  129. data/ext/ngtcp2/examples/tls_client_session_wolfssl.cc +160 -0
  130. data/ext/ngtcp2/examples/tls_client_session_wolfssl.h +52 -0
  131. data/ext/ngtcp2/examples/tls_server_context.h +52 -0
  132. data/ext/ngtcp2/examples/tls_server_context_boringssl.cc +257 -0
  133. data/ext/ngtcp2/examples/tls_server_context_boringssl.h +54 -0
  134. data/ext/ngtcp2/examples/tls_server_context_gnutls.cc +99 -0
  135. data/ext/ngtcp2/examples/tls_server_context_gnutls.h +59 -0
  136. data/ext/ngtcp2/examples/tls_server_context_openssl.cc +338 -0
  137. data/ext/ngtcp2/examples/tls_server_context_openssl.h +54 -0
  138. data/ext/ngtcp2/examples/tls_server_context_picotls.cc +321 -0
  139. data/ext/ngtcp2/examples/tls_server_context_picotls.h +58 -0
  140. data/ext/ngtcp2/examples/tls_server_context_wolfssl.cc +284 -0
  141. data/ext/ngtcp2/examples/tls_server_context_wolfssl.h +55 -0
  142. data/ext/ngtcp2/examples/tls_server_session.h +52 -0
  143. data/ext/ngtcp2/examples/tls_server_session_boringssl.cc +84 -0
  144. data/ext/ngtcp2/examples/tls_server_session_boringssl.h +47 -0
  145. data/ext/ngtcp2/examples/tls_server_session_gnutls.cc +155 -0
  146. data/ext/ngtcp2/examples/tls_server_session_gnutls.h +46 -0
  147. data/ext/ngtcp2/examples/tls_server_session_openssl.cc +54 -0
  148. data/ext/ngtcp2/examples/tls_server_session_openssl.h +47 -0
  149. data/ext/ngtcp2/examples/tls_server_session_picotls.cc +70 -0
  150. data/ext/ngtcp2/examples/tls_server_session_picotls.h +47 -0
  151. data/ext/ngtcp2/examples/tls_server_session_wolfssl.cc +55 -0
  152. data/ext/ngtcp2/examples/tls_server_session_wolfssl.h +47 -0
  153. data/ext/ngtcp2/examples/tls_session_base_gnutls.cc +87 -0
  154. data/ext/ngtcp2/examples/tls_session_base_gnutls.h +51 -0
  155. data/ext/ngtcp2/examples/tls_session_base_openssl.cc +54 -0
  156. data/ext/ngtcp2/examples/tls_session_base_openssl.h +52 -0
  157. data/ext/ngtcp2/examples/tls_session_base_picotls.cc +56 -0
  158. data/ext/ngtcp2/examples/tls_session_base_picotls.h +54 -0
  159. data/ext/ngtcp2/examples/tls_session_base_wolfssl.cc +54 -0
  160. data/ext/ngtcp2/examples/tls_session_base_wolfssl.h +54 -0
  161. data/ext/ngtcp2/examples/tls_shared_picotls.cc +59 -0
  162. data/ext/ngtcp2/examples/tls_shared_picotls.h +36 -0
  163. data/ext/ngtcp2/examples/util.cc +646 -0
  164. data/ext/ngtcp2/examples/util.h +361 -0
  165. data/ext/ngtcp2/examples/util_gnutls.cc +136 -0
  166. data/ext/ngtcp2/examples/util_openssl.cc +131 -0
  167. data/ext/ngtcp2/examples/util_test.cc +237 -0
  168. data/ext/ngtcp2/examples/util_test.h +45 -0
  169. data/ext/ngtcp2/examples/util_wolfssl.cc +130 -0
  170. data/ext/ngtcp2/fuzz/corpus/decode_frame/ack +0 -0
  171. data/ext/ngtcp2/fuzz/corpus/decode_frame/ack_ecn +0 -0
  172. data/ext/ngtcp2/fuzz/corpus/decode_frame/connection_close +0 -0
  173. data/ext/ngtcp2/fuzz/corpus/decode_frame/crypto +1 -0
  174. data/ext/ngtcp2/fuzz/corpus/decode_frame/data_blocked +1 -0
  175. data/ext/ngtcp2/fuzz/corpus/decode_frame/datagram +1 -0
  176. data/ext/ngtcp2/fuzz/corpus/decode_frame/datagram_len +1 -0
  177. data/ext/ngtcp2/fuzz/corpus/decode_frame/max_data +1 -0
  178. data/ext/ngtcp2/fuzz/corpus/decode_frame/max_stream_data +0 -0
  179. data/ext/ngtcp2/fuzz/corpus/decode_frame/max_streams +0 -0
  180. data/ext/ngtcp2/fuzz/corpus/decode_frame/new_connection_id +1 -0
  181. data/ext/ngtcp2/fuzz/corpus/decode_frame/new_token +1 -0
  182. data/ext/ngtcp2/fuzz/corpus/decode_frame/path_challenge +1 -0
  183. data/ext/ngtcp2/fuzz/corpus/decode_frame/path_response +1 -0
  184. data/ext/ngtcp2/fuzz/corpus/decode_frame/reset_stream +0 -0
  185. data/ext/ngtcp2/fuzz/corpus/decode_frame/retire_connection_id +1 -0
  186. data/ext/ngtcp2/fuzz/corpus/decode_frame/stop_sending +0 -0
  187. data/ext/ngtcp2/fuzz/corpus/decode_frame/stream +0 -0
  188. data/ext/ngtcp2/fuzz/corpus/decode_frame/stream_data_blocked +0 -0
  189. data/ext/ngtcp2/fuzz/corpus/decode_frame/stream_len +0 -0
  190. data/ext/ngtcp2/fuzz/corpus/decode_frame/streams_blocked +0 -0
  191. data/ext/ngtcp2/fuzz/corpus/ksl/random +0 -0
  192. data/ext/ngtcp2/fuzz/decode_frame.cc +25 -0
  193. data/ext/ngtcp2/fuzz/ksl.cc +77 -0
  194. data/ext/ngtcp2/interop/Dockerfile +39 -0
  195. data/ext/ngtcp2/interop/run_endpoint.sh +93 -0
  196. data/ext/ngtcp2/lib/CMakeLists.txt +110 -0
  197. data/ext/ngtcp2/lib/Makefile.am +122 -0
  198. data/ext/ngtcp2/lib/includes/CMakeLists.txt +4 -0
  199. data/ext/ngtcp2/lib/includes/Makefile.am +25 -0
  200. data/ext/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +5843 -0
  201. data/ext/ngtcp2/lib/includes/ngtcp2/version.h.in +51 -0
  202. data/ext/ngtcp2/lib/libngtcp2.pc.in +33 -0
  203. data/ext/ngtcp2/lib/ngtcp2_acktr.c +335 -0
  204. data/ext/ngtcp2/lib/ngtcp2_acktr.h +221 -0
  205. data/ext/ngtcp2/lib/ngtcp2_addr.c +117 -0
  206. data/ext/ngtcp2/lib/ngtcp2_addr.h +69 -0
  207. data/ext/ngtcp2/lib/ngtcp2_balloc.c +90 -0
  208. data/ext/ngtcp2/lib/ngtcp2_balloc.h +91 -0
  209. data/ext/ngtcp2/lib/ngtcp2_bbr.c +693 -0
  210. data/ext/ngtcp2/lib/ngtcp2_bbr.h +157 -0
  211. data/ext/ngtcp2/lib/ngtcp2_bbr2.c +1490 -0
  212. data/ext/ngtcp2/lib/ngtcp2_bbr2.h +149 -0
  213. data/ext/ngtcp2/lib/ngtcp2_buf.c +56 -0
  214. data/ext/ngtcp2/lib/ngtcp2_buf.h +108 -0
  215. data/ext/ngtcp2/lib/ngtcp2_cc.c +616 -0
  216. data/ext/ngtcp2/lib/ngtcp2_cc.h +422 -0
  217. data/ext/ngtcp2/lib/ngtcp2_cid.c +147 -0
  218. data/ext/ngtcp2/lib/ngtcp2_cid.h +175 -0
  219. data/ext/ngtcp2/lib/ngtcp2_conn.c +13731 -0
  220. data/ext/ngtcp2/lib/ngtcp2_conn.h +1119 -0
  221. data/ext/ngtcp2/lib/ngtcp2_conn_stat.h +131 -0
  222. data/ext/ngtcp2/lib/ngtcp2_conv.c +291 -0
  223. data/ext/ngtcp2/lib/ngtcp2_conv.h +208 -0
  224. data/ext/ngtcp2/lib/ngtcp2_crypto.c +895 -0
  225. data/ext/ngtcp2/lib/ngtcp2_crypto.h +148 -0
  226. data/ext/ngtcp2/lib/ngtcp2_err.c +154 -0
  227. data/ext/ngtcp2/lib/ngtcp2_err.h +34 -0
  228. data/ext/ngtcp2/lib/ngtcp2_gaptr.c +167 -0
  229. data/ext/ngtcp2/lib/ngtcp2_gaptr.h +98 -0
  230. data/ext/ngtcp2/lib/ngtcp2_idtr.c +79 -0
  231. data/ext/ngtcp2/lib/ngtcp2_idtr.h +89 -0
  232. data/ext/ngtcp2/lib/ngtcp2_ksl.c +819 -0
  233. data/ext/ngtcp2/lib/ngtcp2_ksl.h +345 -0
  234. data/ext/ngtcp2/lib/ngtcp2_log.c +822 -0
  235. data/ext/ngtcp2/lib/ngtcp2_log.h +123 -0
  236. data/ext/ngtcp2/lib/ngtcp2_macro.h +58 -0
  237. data/ext/ngtcp2/lib/ngtcp2_map.c +336 -0
  238. data/ext/ngtcp2/lib/ngtcp2_map.h +136 -0
  239. data/ext/ngtcp2/lib/ngtcp2_mem.c +113 -0
  240. data/ext/ngtcp2/lib/ngtcp2_mem.h +72 -0
  241. data/ext/ngtcp2/lib/ngtcp2_net.h +136 -0
  242. data/ext/ngtcp2/lib/ngtcp2_objalloc.c +40 -0
  243. data/ext/ngtcp2/lib/ngtcp2_objalloc.h +140 -0
  244. data/ext/ngtcp2/lib/ngtcp2_opl.c +46 -0
  245. data/ext/ngtcp2/lib/ngtcp2_opl.h +65 -0
  246. data/ext/ngtcp2/lib/ngtcp2_path.c +77 -0
  247. data/ext/ngtcp2/lib/ngtcp2_path.h +49 -0
  248. data/ext/ngtcp2/lib/ngtcp2_pkt.c +2527 -0
  249. data/ext/ngtcp2/lib/ngtcp2_pkt.h +1235 -0
  250. data/ext/ngtcp2/lib/ngtcp2_pmtud.c +160 -0
  251. data/ext/ngtcp2/lib/ngtcp2_pmtud.h +123 -0
  252. data/ext/ngtcp2/lib/ngtcp2_ppe.c +230 -0
  253. data/ext/ngtcp2/lib/ngtcp2_ppe.h +153 -0
  254. data/ext/ngtcp2/lib/ngtcp2_pq.c +164 -0
  255. data/ext/ngtcp2/lib/ngtcp2_pq.h +126 -0
  256. data/ext/ngtcp2/lib/ngtcp2_pv.c +172 -0
  257. data/ext/ngtcp2/lib/ngtcp2_pv.h +194 -0
  258. data/ext/ngtcp2/lib/ngtcp2_qlog.c +1219 -0
  259. data/ext/ngtcp2/lib/ngtcp2_qlog.h +161 -0
  260. data/ext/ngtcp2/lib/ngtcp2_range.c +61 -0
  261. data/ext/ngtcp2/lib/ngtcp2_range.h +80 -0
  262. data/ext/ngtcp2/lib/ngtcp2_rcvry.h +40 -0
  263. data/ext/ngtcp2/lib/ngtcp2_ringbuf.c +121 -0
  264. data/ext/ngtcp2/lib/ngtcp2_ringbuf.h +132 -0
  265. data/ext/ngtcp2/lib/ngtcp2_rob.c +319 -0
  266. data/ext/ngtcp2/lib/ngtcp2_rob.h +197 -0
  267. data/ext/ngtcp2/lib/ngtcp2_rst.c +138 -0
  268. data/ext/ngtcp2/lib/ngtcp2_rst.h +86 -0
  269. data/ext/ngtcp2/lib/ngtcp2_rtb.c +1676 -0
  270. data/ext/ngtcp2/lib/ngtcp2_rtb.h +468 -0
  271. data/ext/ngtcp2/lib/ngtcp2_str.c +233 -0
  272. data/ext/ngtcp2/lib/ngtcp2_str.h +94 -0
  273. data/ext/ngtcp2/lib/ngtcp2_strm.c +698 -0
  274. data/ext/ngtcp2/lib/ngtcp2_strm.h +310 -0
  275. data/ext/ngtcp2/lib/ngtcp2_unreachable.c +71 -0
  276. data/ext/ngtcp2/lib/ngtcp2_unreachable.h +46 -0
  277. data/ext/ngtcp2/lib/ngtcp2_vec.c +243 -0
  278. data/ext/ngtcp2/lib/ngtcp2_vec.h +120 -0
  279. data/ext/ngtcp2/lib/ngtcp2_version.c +39 -0
  280. data/ext/ngtcp2/lib/ngtcp2_window_filter.c +99 -0
  281. data/ext/ngtcp2/lib/ngtcp2_window_filter.h +65 -0
  282. data/ext/ngtcp2/m4/ax_check_compile_flag.m4 +74 -0
  283. data/ext/ngtcp2/m4/ax_cxx_compile_stdcxx.m4 +1009 -0
  284. data/ext/ngtcp2/tests/CMakeLists.txt +68 -0
  285. data/ext/ngtcp2/tests/Makefile.am +94 -0
  286. data/ext/ngtcp2/tests/main.c +358 -0
  287. data/ext/ngtcp2/tests/ngtcp2_acktr_test.c +367 -0
  288. data/ext/ngtcp2/tests/ngtcp2_acktr_test.h +37 -0
  289. data/ext/ngtcp2/tests/ngtcp2_conn_test.c +9821 -0
  290. data/ext/ngtcp2/tests/ngtcp2_conn_test.h +104 -0
  291. data/ext/ngtcp2/tests/ngtcp2_conv_test.c +430 -0
  292. data/ext/ngtcp2/tests/ngtcp2_conv_test.h +46 -0
  293. data/ext/ngtcp2/tests/ngtcp2_crypto_test.c +667 -0
  294. data/ext/ngtcp2/tests/ngtcp2_crypto_test.h +35 -0
  295. data/ext/ngtcp2/tests/ngtcp2_gaptr_test.c +127 -0
  296. data/ext/ngtcp2/tests/ngtcp2_gaptr_test.h +36 -0
  297. data/ext/ngtcp2/tests/ngtcp2_idtr_test.c +79 -0
  298. data/ext/ngtcp2/tests/ngtcp2_idtr_test.h +34 -0
  299. data/ext/ngtcp2/tests/ngtcp2_ksl_test.c +502 -0
  300. data/ext/ngtcp2/tests/ngtcp2_ksl_test.h +39 -0
  301. data/ext/ngtcp2/tests/ngtcp2_map_test.c +206 -0
  302. data/ext/ngtcp2/tests/ngtcp2_map_test.h +38 -0
  303. data/ext/ngtcp2/tests/ngtcp2_pkt_test.c +1645 -0
  304. data/ext/ngtcp2/tests/ngtcp2_pkt_test.h +68 -0
  305. data/ext/ngtcp2/tests/ngtcp2_pmtud_test.c +153 -0
  306. data/ext/ngtcp2/tests/ngtcp2_pmtud_test.h +34 -0
  307. data/ext/ngtcp2/tests/ngtcp2_pv_test.c +129 -0
  308. data/ext/ngtcp2/tests/ngtcp2_pv_test.h +35 -0
  309. data/ext/ngtcp2/tests/ngtcp2_range_test.c +105 -0
  310. data/ext/ngtcp2/tests/ngtcp2_range_test.h +36 -0
  311. data/ext/ngtcp2/tests/ngtcp2_ringbuf_test.c +91 -0
  312. data/ext/ngtcp2/tests/ngtcp2_ringbuf_test.h +35 -0
  313. data/ext/ngtcp2/tests/ngtcp2_rob_test.c +552 -0
  314. data/ext/ngtcp2/tests/ngtcp2_rob_test.h +37 -0
  315. data/ext/ngtcp2/tests/ngtcp2_rtb_test.c +470 -0
  316. data/ext/ngtcp2/tests/ngtcp2_rtb_test.h +38 -0
  317. data/ext/ngtcp2/tests/ngtcp2_str_test.c +96 -0
  318. data/ext/ngtcp2/tests/ngtcp2_str_test.h +36 -0
  319. data/ext/ngtcp2/tests/ngtcp2_strm_test.c +575 -0
  320. data/ext/ngtcp2/tests/ngtcp2_strm_test.h +36 -0
  321. data/ext/ngtcp2/tests/ngtcp2_test_helper.c +404 -0
  322. data/ext/ngtcp2/tests/ngtcp2_test_helper.h +191 -0
  323. data/ext/ngtcp2/tests/ngtcp2_vec_test.c +426 -0
  324. data/ext/ngtcp2/tests/ngtcp2_vec_test.h +36 -0
  325. data/ext/ngtcp2/third-party/CMakeLists.txt +34 -0
  326. data/ext/ngtcp2/third-party/Makefile.am +31 -0
  327. data/ext/ngtcp2/third-party/http-parser/AUTHORS +68 -0
  328. data/ext/ngtcp2/third-party/http-parser/LICENSE-MIT +23 -0
  329. data/ext/ngtcp2/third-party/http-parser/Makefile +157 -0
  330. data/ext/ngtcp2/third-party/http-parser/README.md +246 -0
  331. data/ext/ngtcp2/third-party/http-parser/bench.c +111 -0
  332. data/ext/ngtcp2/third-party/http-parser/contrib/parsertrace.c +160 -0
  333. data/ext/ngtcp2/third-party/http-parser/contrib/url_parser.c +47 -0
  334. data/ext/ngtcp2/third-party/http-parser/http_parser.c +2419 -0
  335. data/ext/ngtcp2/third-party/http-parser/http_parser.gyp +111 -0
  336. data/ext/ngtcp2/third-party/http-parser/http_parser.h +431 -0
  337. data/ext/ngtcp2/third-party/http-parser/test.c +4411 -0
  338. data/lib/protocol/quic/version.rb +10 -0
  339. data/lib/protocol/quic.rb +9 -0
  340. data/license.md +21 -0
  341. data.tar.gz.sig +1 -0
  342. metadata +424 -0
  343. metadata.gz.sig +1 -0
@@ -0,0 +1,3049 @@
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 <cstdlib>
26
+ #include <cassert>
27
+ #include <cerrno>
28
+ #include <iostream>
29
+ #include <algorithm>
30
+ #include <memory>
31
+ #include <fstream>
32
+ #include <iomanip>
33
+
34
+ #include <unistd.h>
35
+ #include <getopt.h>
36
+ #include <sys/types.h>
37
+ #include <sys/stat.h>
38
+ #include <fcntl.h>
39
+ #include <sys/socket.h>
40
+ #include <netdb.h>
41
+ #include <sys/mman.h>
42
+
43
+ #include <http-parser/http_parser.h>
44
+
45
+ #include "client.h"
46
+ #include "network.h"
47
+ #include "debug.h"
48
+ #include "util.h"
49
+ #include "shared.h"
50
+
51
+ using namespace ngtcp2;
52
+ using namespace std::literals;
53
+
54
+ namespace {
55
+ auto randgen = util::make_mt19937();
56
+ } // namespace
57
+
58
+ namespace {
59
+ constexpr size_t max_preferred_versionslen = 4;
60
+ } // namespace
61
+
62
+ Config config{};
63
+
64
+ Stream::Stream(const Request &req, int64_t stream_id)
65
+ : req(req), stream_id(stream_id), fd(-1) {}
66
+
67
+ Stream::~Stream() {
68
+ if (fd != -1) {
69
+ close(fd);
70
+ }
71
+ }
72
+
73
+ int Stream::open_file(const std::string_view &path) {
74
+ assert(fd == -1);
75
+
76
+ std::string_view filename;
77
+
78
+ auto it = std::find(std::rbegin(path), std::rend(path), '/').base();
79
+ if (it == std::end(path)) {
80
+ filename = "index.html"sv;
81
+ } else {
82
+ filename = std::string_view{it, static_cast<size_t>(std::end(path) - it)};
83
+ if (filename == ".."sv || filename == "."sv) {
84
+ std::cerr << "Invalid file name: " << filename << std::endl;
85
+ return -1;
86
+ }
87
+ }
88
+
89
+ auto fname = std::string{config.download};
90
+ fname += '/';
91
+ fname += filename;
92
+
93
+ fd = open(fname.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
94
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
95
+ if (fd == -1) {
96
+ std::cerr << "open: Could not open file " << fname << ": "
97
+ << strerror(errno) << std::endl;
98
+ return -1;
99
+ }
100
+
101
+ return 0;
102
+ }
103
+
104
+ namespace {
105
+ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
106
+ auto c = static_cast<Client *>(w->data);
107
+
108
+ c->on_write();
109
+ }
110
+ } // namespace
111
+
112
+ namespace {
113
+ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
114
+ auto ep = static_cast<Endpoint *>(w->data);
115
+ auto c = ep->client;
116
+
117
+ if (c->on_read(*ep) != 0) {
118
+ return;
119
+ }
120
+
121
+ c->on_write();
122
+ }
123
+ } // namespace
124
+
125
+ namespace {
126
+ void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
127
+ int rv;
128
+ auto c = static_cast<Client *>(w->data);
129
+
130
+ rv = c->handle_expiry();
131
+ if (rv != 0) {
132
+ return;
133
+ }
134
+
135
+ c->on_write();
136
+ }
137
+ } // namespace
138
+
139
+ namespace {
140
+ void change_local_addrcb(struct ev_loop *loop, ev_timer *w, int revents) {
141
+ auto c = static_cast<Client *>(w->data);
142
+
143
+ c->change_local_addr();
144
+ }
145
+ } // namespace
146
+
147
+ namespace {
148
+ void key_updatecb(struct ev_loop *loop, ev_timer *w, int revents) {
149
+ auto c = static_cast<Client *>(w->data);
150
+
151
+ if (c->initiate_key_update() != 0) {
152
+ c->disconnect();
153
+ }
154
+ }
155
+ } // namespace
156
+
157
+ namespace {
158
+ void delay_streamcb(struct ev_loop *loop, ev_timer *w, int revents) {
159
+ auto c = static_cast<Client *>(w->data);
160
+
161
+ ev_timer_stop(loop, w);
162
+ c->on_extend_max_streams();
163
+ c->on_write();
164
+ }
165
+ } // namespace
166
+
167
+ namespace {
168
+ void siginthandler(struct ev_loop *loop, ev_signal *w, int revents) {
169
+ ev_break(loop, EVBREAK_ALL);
170
+ }
171
+ } // namespace
172
+
173
+ Client::Client(struct ev_loop *loop, uint32_t client_chosen_version,
174
+ uint32_t original_version)
175
+ : remote_addr_{},
176
+ loop_(loop),
177
+ httpconn_(nullptr),
178
+ addr_(nullptr),
179
+ port_(nullptr),
180
+ nstreams_done_(0),
181
+ nstreams_closed_(0),
182
+ nkey_update_(0),
183
+ client_chosen_version_(client_chosen_version),
184
+ original_version_(original_version),
185
+ early_data_(false),
186
+ should_exit_(false),
187
+ should_exit_on_handshake_confirmed_(false),
188
+ handshake_confirmed_(false),
189
+ tx_{} {
190
+ ev_io_init(&wev_, writecb, 0, EV_WRITE);
191
+ wev_.data = this;
192
+ ev_timer_init(&timer_, timeoutcb, 0., 0.);
193
+ timer_.data = this;
194
+ ev_timer_init(&change_local_addr_timer_, change_local_addrcb,
195
+ static_cast<double>(config.change_local_addr) / NGTCP2_SECONDS,
196
+ 0.);
197
+ change_local_addr_timer_.data = this;
198
+ ev_timer_init(&key_update_timer_, key_updatecb,
199
+ static_cast<double>(config.key_update) / NGTCP2_SECONDS, 0.);
200
+ key_update_timer_.data = this;
201
+ ev_timer_init(&delay_stream_timer_, delay_streamcb,
202
+ static_cast<double>(config.delay_stream) / NGTCP2_SECONDS, 0.);
203
+ delay_stream_timer_.data = this;
204
+ ev_signal_init(&sigintev_, siginthandler, SIGINT);
205
+ }
206
+
207
+ Client::~Client() {
208
+ disconnect();
209
+
210
+ if (httpconn_) {
211
+ nghttp3_conn_del(httpconn_);
212
+ httpconn_ = nullptr;
213
+ }
214
+ }
215
+
216
+ void Client::disconnect() {
217
+ tx_.send_blocked = false;
218
+
219
+ handle_error();
220
+
221
+ config.tx_loss_prob = 0;
222
+
223
+ ev_timer_stop(loop_, &delay_stream_timer_);
224
+ ev_timer_stop(loop_, &key_update_timer_);
225
+ ev_timer_stop(loop_, &change_local_addr_timer_);
226
+ ev_timer_stop(loop_, &timer_);
227
+
228
+ ev_io_stop(loop_, &wev_);
229
+
230
+ for (auto &ep : endpoints_) {
231
+ ev_io_stop(loop_, &ep.rev);
232
+ close(ep.fd);
233
+ }
234
+
235
+ endpoints_.clear();
236
+
237
+ ev_signal_stop(loop_, &sigintev_);
238
+ }
239
+
240
+ namespace {
241
+ int recv_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
242
+ uint64_t offset, const uint8_t *data, size_t datalen,
243
+ void *user_data) {
244
+ if (!config.quiet && !config.no_quic_dump) {
245
+ debug::print_crypto_data(crypto_level, data, datalen);
246
+ }
247
+
248
+ return ngtcp2_crypto_recv_crypto_data_cb(conn, crypto_level, offset, data,
249
+ datalen, user_data);
250
+ }
251
+ } // namespace
252
+
253
+ namespace {
254
+ int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
255
+ uint64_t offset, const uint8_t *data, size_t datalen,
256
+ void *user_data, void *stream_user_data) {
257
+ if (!config.quiet && !config.no_quic_dump) {
258
+ debug::print_stream_data(stream_id, data, datalen);
259
+ }
260
+
261
+ auto c = static_cast<Client *>(user_data);
262
+
263
+ if (c->recv_stream_data(flags, stream_id, data, datalen) != 0) {
264
+ return NGTCP2_ERR_CALLBACK_FAILURE;
265
+ }
266
+
267
+ return 0;
268
+ }
269
+ } // namespace
270
+
271
+ namespace {
272
+ int acked_stream_data_offset(ngtcp2_conn *conn, int64_t stream_id,
273
+ uint64_t offset, uint64_t datalen, void *user_data,
274
+ void *stream_user_data) {
275
+ auto c = static_cast<Client *>(user_data);
276
+ if (c->acked_stream_data_offset(stream_id, datalen) != 0) {
277
+ return NGTCP2_ERR_CALLBACK_FAILURE;
278
+ }
279
+ return 0;
280
+ }
281
+ } // namespace
282
+
283
+ namespace {
284
+ int handshake_completed(ngtcp2_conn *conn, void *user_data) {
285
+ auto c = static_cast<Client *>(user_data);
286
+
287
+ if (!config.quiet) {
288
+ debug::handshake_completed(conn, user_data);
289
+ }
290
+
291
+ if (c->handshake_completed() != 0) {
292
+ return NGTCP2_ERR_CALLBACK_FAILURE;
293
+ }
294
+
295
+ return 0;
296
+ }
297
+ } // namespace
298
+
299
+ int Client::handshake_completed() {
300
+ if (early_data_ && !tls_session_.get_early_data_accepted()) {
301
+ if (!config.quiet) {
302
+ std::cerr << "Early data was rejected by server" << std::endl;
303
+ }
304
+
305
+ // Some TLS backends only report early data rejection after
306
+ // handshake completion (e.g., OpenSSL). For TLS backends which
307
+ // report it early (e.g., BoringSSL and PicoTLS), the following
308
+ // functions are noop.
309
+ if (auto rv = ngtcp2_conn_early_data_rejected(conn_); rv != 0) {
310
+ std::cerr << "ngtcp2_conn_early_data_rejected: " << ngtcp2_strerror(rv)
311
+ << std::endl;
312
+ return -1;
313
+ }
314
+
315
+ if (setup_httpconn() != 0) {
316
+ return -1;
317
+ }
318
+ }
319
+
320
+ if (!config.quiet) {
321
+ std::cerr << "Negotiated cipher suite is " << tls_session_.get_cipher_name()
322
+ << std::endl;
323
+ std::cerr << "Negotiated ALPN is " << tls_session_.get_selected_alpn()
324
+ << std::endl;
325
+ }
326
+
327
+ if (config.tp_file) {
328
+ auto params = ngtcp2_conn_get_remote_transport_params(conn_);
329
+
330
+ if (write_transport_params(config.tp_file, params) != 0) {
331
+ std::cerr << "Could not write transport parameters in " << config.tp_file
332
+ << std::endl;
333
+ }
334
+ }
335
+
336
+ return 0;
337
+ }
338
+
339
+ namespace {
340
+ int handshake_confirmed(ngtcp2_conn *conn, void *user_data) {
341
+ auto c = static_cast<Client *>(user_data);
342
+
343
+ if (!config.quiet) {
344
+ debug::handshake_confirmed(conn, user_data);
345
+ }
346
+
347
+ if (c->handshake_confirmed() != 0) {
348
+ return NGTCP2_ERR_CALLBACK_FAILURE;
349
+ }
350
+
351
+ return 0;
352
+ }
353
+ } // namespace
354
+
355
+ int Client::handshake_confirmed() {
356
+ handshake_confirmed_ = true;
357
+
358
+ if (config.change_local_addr) {
359
+ start_change_local_addr_timer();
360
+ }
361
+ if (config.key_update) {
362
+ start_key_update_timer();
363
+ }
364
+ if (config.delay_stream) {
365
+ start_delay_stream_timer();
366
+ }
367
+
368
+ if (should_exit_on_handshake_confirmed_) {
369
+ should_exit_ = true;
370
+ }
371
+
372
+ return 0;
373
+ }
374
+
375
+ namespace {
376
+ int recv_version_negotiation(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
377
+ const uint32_t *sv, size_t nsv, void *user_data) {
378
+ auto c = static_cast<Client *>(user_data);
379
+
380
+ c->recv_version_negotiation(sv, nsv);
381
+
382
+ return 0;
383
+ }
384
+ } // namespace
385
+
386
+ void Client::recv_version_negotiation(const uint32_t *sv, size_t nsv) {
387
+ offered_versions_.resize(nsv);
388
+ std::copy_n(sv, nsv, std::begin(offered_versions_));
389
+ }
390
+
391
+ namespace {
392
+ int stream_close(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
393
+ uint64_t app_error_code, void *user_data,
394
+ void *stream_user_data) {
395
+ auto c = static_cast<Client *>(user_data);
396
+
397
+ if (!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
398
+ app_error_code = NGHTTP3_H3_NO_ERROR;
399
+ }
400
+
401
+ if (c->on_stream_close(stream_id, app_error_code) != 0) {
402
+ return NGTCP2_ERR_CALLBACK_FAILURE;
403
+ }
404
+
405
+ return 0;
406
+ }
407
+ } // namespace
408
+
409
+ namespace {
410
+ int stream_reset(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size,
411
+ uint64_t app_error_code, void *user_data,
412
+ void *stream_user_data) {
413
+ auto c = static_cast<Client *>(user_data);
414
+
415
+ if (c->on_stream_reset(stream_id) != 0) {
416
+ return NGTCP2_ERR_CALLBACK_FAILURE;
417
+ }
418
+
419
+ return 0;
420
+ }
421
+ } // namespace
422
+
423
+ namespace {
424
+ int stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id,
425
+ uint64_t app_error_code, void *user_data,
426
+ void *stream_user_data) {
427
+ auto c = static_cast<Client *>(user_data);
428
+
429
+ if (c->on_stream_stop_sending(stream_id) != 0) {
430
+ return NGTCP2_ERR_CALLBACK_FAILURE;
431
+ }
432
+
433
+ return 0;
434
+ }
435
+ } // namespace
436
+
437
+ namespace {
438
+ int extend_max_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams,
439
+ void *user_data) {
440
+ auto c = static_cast<Client *>(user_data);
441
+
442
+ if (c->on_extend_max_streams() != 0) {
443
+ return NGTCP2_ERR_CALLBACK_FAILURE;
444
+ }
445
+
446
+ return 0;
447
+ }
448
+ } // namespace
449
+
450
+ namespace {
451
+ void rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx) {
452
+ auto dis = std::uniform_int_distribution<uint8_t>();
453
+ std::generate(dest, dest + destlen, [&dis]() { return dis(randgen); });
454
+ }
455
+ } // namespace
456
+
457
+ namespace {
458
+ int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, uint8_t *token,
459
+ size_t cidlen, void *user_data) {
460
+ if (util::generate_secure_random(cid->data, cidlen) != 0) {
461
+ return NGTCP2_ERR_CALLBACK_FAILURE;
462
+ }
463
+
464
+ cid->datalen = cidlen;
465
+ if (ngtcp2_crypto_generate_stateless_reset_token(
466
+ token, config.static_secret.data(), config.static_secret.size(),
467
+ cid) != 0) {
468
+ return NGTCP2_ERR_CALLBACK_FAILURE;
469
+ }
470
+
471
+ return 0;
472
+ }
473
+ } // namespace
474
+
475
+ namespace {
476
+ int do_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
477
+ const ngtcp2_crypto_cipher_ctx *hp_ctx, const uint8_t *sample) {
478
+ if (ngtcp2_crypto_hp_mask(dest, hp, hp_ctx, sample) != 0) {
479
+ return NGTCP2_ERR_CALLBACK_FAILURE;
480
+ }
481
+
482
+ if (!config.quiet && config.show_secret) {
483
+ debug::print_hp_mask(dest, NGTCP2_HP_MASKLEN, sample, NGTCP2_HP_SAMPLELEN);
484
+ }
485
+
486
+ return 0;
487
+ }
488
+ } // namespace
489
+
490
+ namespace {
491
+ int update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
492
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
493
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
494
+ const uint8_t *current_rx_secret,
495
+ const uint8_t *current_tx_secret, size_t secretlen,
496
+ void *user_data) {
497
+ auto c = static_cast<Client *>(user_data);
498
+
499
+ if (c->update_key(rx_secret, tx_secret, rx_aead_ctx, rx_iv, tx_aead_ctx,
500
+ tx_iv, current_rx_secret, current_tx_secret,
501
+ secretlen) != 0) {
502
+ return NGTCP2_ERR_CALLBACK_FAILURE;
503
+ }
504
+
505
+ return 0;
506
+ }
507
+ } // namespace
508
+
509
+ namespace {
510
+ int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path,
511
+ ngtcp2_path_validation_result res, void *user_data) {
512
+ if (!config.quiet) {
513
+ debug::path_validation(path, res);
514
+ }
515
+
516
+ if (flags & NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR) {
517
+ auto c = static_cast<Client *>(user_data);
518
+
519
+ c->set_remote_addr(path->remote);
520
+ }
521
+
522
+ return 0;
523
+ }
524
+ } // namespace
525
+
526
+ void Client::set_remote_addr(const ngtcp2_addr &remote_addr) {
527
+ memcpy(&remote_addr_.su, remote_addr.addr, remote_addr.addrlen);
528
+ remote_addr_.len = remote_addr.addrlen;
529
+ }
530
+
531
+ namespace {
532
+ int select_preferred_address(ngtcp2_conn *conn, ngtcp2_path *dest,
533
+ const ngtcp2_preferred_addr *paddr,
534
+ void *user_data) {
535
+ auto c = static_cast<Client *>(user_data);
536
+ Address remote_addr;
537
+
538
+ if (config.no_preferred_addr) {
539
+ return 0;
540
+ }
541
+
542
+ if (c->select_preferred_address(remote_addr, paddr) != 0) {
543
+ return 0;
544
+ }
545
+
546
+ auto ep = c->endpoint_for(remote_addr);
547
+ if (!ep) {
548
+ return NGTCP2_ERR_CALLBACK_FAILURE;
549
+ }
550
+
551
+ ngtcp2_addr_copy_byte(&dest->local, &(*ep)->addr.su.sa, (*ep)->addr.len);
552
+ ngtcp2_addr_copy_byte(&dest->remote, &remote_addr.su.sa, remote_addr.len);
553
+ dest->user_data = *ep;
554
+
555
+ return 0;
556
+ }
557
+ } // namespace
558
+
559
+ namespace {
560
+ int extend_max_stream_data(ngtcp2_conn *conn, int64_t stream_id,
561
+ uint64_t max_data, void *user_data,
562
+ void *stream_user_data) {
563
+ auto c = static_cast<Client *>(user_data);
564
+ if (c->extend_max_stream_data(stream_id, max_data) != 0) {
565
+ return NGTCP2_ERR_CALLBACK_FAILURE;
566
+ }
567
+ return 0;
568
+ }
569
+ } // namespace
570
+
571
+ int Client::extend_max_stream_data(int64_t stream_id, uint64_t max_data) {
572
+ if (auto rv = nghttp3_conn_unblock_stream(httpconn_, stream_id); rv != 0) {
573
+ std::cerr << "nghttp3_conn_unblock_stream: " << nghttp3_strerror(rv)
574
+ << std::endl;
575
+ return -1;
576
+ }
577
+ return 0;
578
+ }
579
+
580
+ namespace {
581
+ int recv_new_token(ngtcp2_conn *conn, const uint8_t *token, size_t tokenlen,
582
+ void *user_data) {
583
+ if (config.token_file.empty()) {
584
+ return 0;
585
+ }
586
+
587
+ util::write_token(config.token_file, token, tokenlen);
588
+
589
+ return 0;
590
+ }
591
+ } // namespace
592
+
593
+ namespace {
594
+ int recv_rx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level, void *user_data) {
595
+ if (level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
596
+ return 0;
597
+ }
598
+
599
+ auto c = static_cast<Client *>(user_data);
600
+ if ((!c->get_early_data() || ngtcp2_conn_get_early_data_rejected(conn)) &&
601
+ c->setup_httpconn() != 0) {
602
+ return NGTCP2_ERR_CALLBACK_FAILURE;
603
+ }
604
+
605
+ return 0;
606
+ }
607
+ } // namespace
608
+
609
+ namespace {
610
+ int early_data_rejected(ngtcp2_conn *conn, void *user_data) {
611
+ auto c = static_cast<Client *>(user_data);
612
+
613
+ c->early_data_rejected();
614
+
615
+ return 0;
616
+ }
617
+ } // namespace
618
+
619
+ void Client::early_data_rejected() {
620
+ nghttp3_conn_del(httpconn_);
621
+ httpconn_ = nullptr;
622
+
623
+ nstreams_done_ = 0;
624
+ streams_.clear();
625
+ }
626
+
627
+ int Client::init(int fd, const Address &local_addr, const Address &remote_addr,
628
+ const char *addr, const char *port,
629
+ TLSClientContext &tls_ctx) {
630
+ endpoints_.reserve(4);
631
+
632
+ endpoints_.emplace_back();
633
+ auto &ep = endpoints_.back();
634
+ ep.addr = local_addr;
635
+ ep.client = this;
636
+ ep.fd = fd;
637
+ ev_io_init(&ep.rev, readcb, fd, EV_READ);
638
+ ep.rev.data = &ep;
639
+
640
+ remote_addr_ = remote_addr;
641
+ addr_ = addr;
642
+ port_ = port;
643
+
644
+ auto callbacks = ngtcp2_callbacks{
645
+ ngtcp2_crypto_client_initial_cb,
646
+ nullptr, // recv_client_initial
647
+ ::recv_crypto_data,
648
+ ::handshake_completed,
649
+ ::recv_version_negotiation,
650
+ ngtcp2_crypto_encrypt_cb,
651
+ ngtcp2_crypto_decrypt_cb,
652
+ do_hp_mask,
653
+ ::recv_stream_data,
654
+ ::acked_stream_data_offset,
655
+ nullptr, // stream_open
656
+ stream_close,
657
+ nullptr, // recv_stateless_reset
658
+ ngtcp2_crypto_recv_retry_cb,
659
+ extend_max_streams_bidi,
660
+ nullptr, // extend_max_streams_uni
661
+ rand,
662
+ get_new_connection_id,
663
+ nullptr, // remove_connection_id
664
+ ::update_key,
665
+ path_validation,
666
+ ::select_preferred_address,
667
+ stream_reset,
668
+ nullptr, // extend_max_remote_streams_bidi,
669
+ nullptr, // extend_max_remote_streams_uni,
670
+ ::extend_max_stream_data,
671
+ nullptr, // dcid_status
672
+ ::handshake_confirmed,
673
+ ::recv_new_token,
674
+ ngtcp2_crypto_delete_crypto_aead_ctx_cb,
675
+ ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
676
+ nullptr, // recv_datagram
677
+ nullptr, // ack_datagram
678
+ nullptr, // lost_datagram
679
+ ngtcp2_crypto_get_path_challenge_data_cb,
680
+ stream_stop_sending,
681
+ ngtcp2_crypto_version_negotiation_cb,
682
+ ::recv_rx_key,
683
+ nullptr, // recv_tx_key
684
+ ::early_data_rejected,
685
+ };
686
+
687
+ ngtcp2_cid scid, dcid;
688
+ if (config.scid_present) {
689
+ scid = config.scid;
690
+ } else {
691
+ scid.datalen = 17;
692
+ if (util::generate_secure_random(scid.data, scid.datalen) != 0) {
693
+ std::cerr << "Could not generate source connection ID" << std::endl;
694
+ return -1;
695
+ }
696
+ }
697
+ if (config.dcid.datalen == 0) {
698
+ dcid.datalen = 18;
699
+ if (util::generate_secure_random(dcid.data, dcid.datalen) != 0) {
700
+ std::cerr << "Could not generate destination connection ID" << std::endl;
701
+ return -1;
702
+ }
703
+ } else {
704
+ dcid = config.dcid;
705
+ }
706
+
707
+ ngtcp2_settings settings;
708
+ ngtcp2_settings_default(&settings);
709
+ settings.log_printf = config.quiet ? nullptr : debug::log_printf;
710
+ if (!config.qlog_file.empty() || !config.qlog_dir.empty()) {
711
+ std::string path;
712
+ if (!config.qlog_file.empty()) {
713
+ path = config.qlog_file;
714
+ } else {
715
+ path = std::string{config.qlog_dir};
716
+ path += '/';
717
+ path += util::format_hex(scid.data, scid.datalen);
718
+ path += ".sqlog";
719
+ }
720
+ qlog_ = fopen(path.c_str(), "w");
721
+ if (qlog_ == nullptr) {
722
+ std::cerr << "Could not open qlog file " << std::quoted(path) << ": "
723
+ << strerror(errno) << std::endl;
724
+ return -1;
725
+ }
726
+ settings.qlog.write = qlog_write_cb;
727
+ }
728
+
729
+ settings.cc_algo = config.cc_algo;
730
+ settings.initial_ts = util::timestamp(loop_);
731
+ settings.initial_rtt = config.initial_rtt;
732
+ settings.max_window = config.max_window;
733
+ settings.max_stream_window = config.max_stream_window;
734
+ if (config.max_udp_payload_size) {
735
+ settings.max_tx_udp_payload_size = config.max_udp_payload_size;
736
+ settings.no_tx_udp_payload_size_shaping = 1;
737
+ }
738
+ settings.handshake_timeout = config.handshake_timeout;
739
+ settings.no_pmtud = config.no_pmtud;
740
+ settings.ack_thresh = config.ack_thresh;
741
+
742
+ std::string token;
743
+
744
+ if (!config.token_file.empty()) {
745
+ std::cerr << "Reading token file " << config.token_file << std::endl;
746
+
747
+ auto t = util::read_token(config.token_file);
748
+ if (t) {
749
+ token = std::move(*t);
750
+ settings.token = reinterpret_cast<const uint8_t *>(token.data());
751
+ settings.tokenlen = token.size();
752
+ }
753
+ }
754
+
755
+ if (!config.available_versions.empty()) {
756
+ settings.available_versions = config.available_versions.data();
757
+ settings.available_versionslen = config.available_versions.size();
758
+ }
759
+
760
+ if (!config.preferred_versions.empty()) {
761
+ settings.preferred_versions = config.preferred_versions.data();
762
+ settings.preferred_versionslen = config.preferred_versions.size();
763
+ }
764
+
765
+ settings.original_version = original_version_;
766
+
767
+ ngtcp2_transport_params params;
768
+ ngtcp2_transport_params_default(&params);
769
+ params.initial_max_stream_data_bidi_local = config.max_stream_data_bidi_local;
770
+ params.initial_max_stream_data_bidi_remote =
771
+ config.max_stream_data_bidi_remote;
772
+ params.initial_max_stream_data_uni = config.max_stream_data_uni;
773
+ params.initial_max_data = config.max_data;
774
+ params.initial_max_streams_bidi = config.max_streams_bidi;
775
+ params.initial_max_streams_uni = config.max_streams_uni;
776
+ params.max_idle_timeout = config.timeout;
777
+ params.active_connection_id_limit = 7;
778
+
779
+ auto path = ngtcp2_path{
780
+ {
781
+ const_cast<sockaddr *>(&ep.addr.su.sa),
782
+ ep.addr.len,
783
+ },
784
+ {
785
+ const_cast<sockaddr *>(&remote_addr.su.sa),
786
+ remote_addr.len,
787
+ },
788
+ &ep,
789
+ };
790
+ auto rv = ngtcp2_conn_client_new(&conn_, &dcid, &scid, &path,
791
+ client_chosen_version_, &callbacks,
792
+ &settings, &params, nullptr, this);
793
+
794
+ if (rv != 0) {
795
+ std::cerr << "ngtcp2_conn_client_new: " << ngtcp2_strerror(rv) << std::endl;
796
+ return -1;
797
+ }
798
+
799
+ if (tls_session_.init(early_data_, tls_ctx, addr_, this,
800
+ client_chosen_version_, AppProtocol::H3) != 0) {
801
+ return -1;
802
+ }
803
+
804
+ ngtcp2_conn_set_tls_native_handle(conn_, tls_session_.get_native_handle());
805
+
806
+ if (early_data_ && config.tp_file) {
807
+ ngtcp2_transport_params params;
808
+ if (read_transport_params(config.tp_file, &params) != 0) {
809
+ std::cerr << "Could not read transport parameters from " << config.tp_file
810
+ << std::endl;
811
+ early_data_ = false;
812
+ } else {
813
+ ngtcp2_conn_set_early_remote_transport_params(conn_, &params);
814
+ if (make_stream_early() != 0) {
815
+ return -1;
816
+ }
817
+ }
818
+ }
819
+
820
+ ev_io_start(loop_, &ep.rev);
821
+
822
+ ev_signal_start(loop_, &sigintev_);
823
+
824
+ return 0;
825
+ }
826
+
827
+ int Client::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
828
+ const ngtcp2_pkt_info *pi, uint8_t *data,
829
+ size_t datalen) {
830
+ auto path = ngtcp2_path{
831
+ {
832
+ const_cast<sockaddr *>(&ep.addr.su.sa),
833
+ ep.addr.len,
834
+ },
835
+ {
836
+ const_cast<sockaddr *>(sa),
837
+ salen,
838
+ },
839
+ const_cast<Endpoint *>(&ep),
840
+ };
841
+ if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, pi, data, datalen,
842
+ util::timestamp(loop_));
843
+ rv != 0) {
844
+ std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
845
+ if (!last_error_.error_code) {
846
+ if (rv == NGTCP2_ERR_CRYPTO) {
847
+ ngtcp2_connection_close_error_set_transport_error_tls_alert(
848
+ &last_error_, ngtcp2_conn_get_tls_alert(conn_), nullptr, 0);
849
+ } else {
850
+ ngtcp2_connection_close_error_set_transport_error_liberr(
851
+ &last_error_, rv, nullptr, 0);
852
+ }
853
+ }
854
+ disconnect();
855
+ return -1;
856
+ }
857
+ return 0;
858
+ }
859
+
860
+ int Client::on_read(const Endpoint &ep) {
861
+ std::array<uint8_t, 64_k> buf;
862
+ sockaddr_union su;
863
+ size_t pktcnt = 0;
864
+ ngtcp2_pkt_info pi;
865
+
866
+ iovec msg_iov;
867
+ msg_iov.iov_base = buf.data();
868
+ msg_iov.iov_len = buf.size();
869
+
870
+ msghdr msg{};
871
+ msg.msg_name = &su;
872
+ msg.msg_iov = &msg_iov;
873
+ msg.msg_iovlen = 1;
874
+
875
+ uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint8_t))];
876
+ msg.msg_control = msg_ctrl;
877
+
878
+ for (;;) {
879
+ msg.msg_namelen = sizeof(su);
880
+ msg.msg_controllen = sizeof(msg_ctrl);
881
+
882
+ auto nread = recvmsg(ep.fd, &msg, 0);
883
+
884
+ if (nread == -1) {
885
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
886
+ std::cerr << "recvmsg: " << strerror(errno) << std::endl;
887
+ }
888
+ break;
889
+ }
890
+
891
+ pi.ecn = msghdr_get_ecn(&msg, su.storage.ss_family);
892
+
893
+ if (!config.quiet) {
894
+ std::cerr << "Received packet: local="
895
+ << util::straddr(&ep.addr.su.sa, ep.addr.len)
896
+ << " remote=" << util::straddr(&su.sa, msg.msg_namelen)
897
+ << " ecn=0x" << std::hex << pi.ecn << std::dec << " " << nread
898
+ << " bytes" << std::endl;
899
+ }
900
+
901
+ if (debug::packet_lost(config.rx_loss_prob)) {
902
+ if (!config.quiet) {
903
+ std::cerr << "** Simulated incoming packet loss **" << std::endl;
904
+ }
905
+ break;
906
+ }
907
+
908
+ if (feed_data(ep, &su.sa, msg.msg_namelen, &pi, buf.data(), nread) != 0) {
909
+ return -1;
910
+ }
911
+
912
+ if (++pktcnt >= 10) {
913
+ break;
914
+ }
915
+ }
916
+
917
+ if (should_exit_) {
918
+ ngtcp2_connection_close_error_set_application_error(
919
+ &last_error_, nghttp3_err_infer_quic_app_error_code(0), nullptr, 0);
920
+ disconnect();
921
+ return -1;
922
+ }
923
+
924
+ update_timer();
925
+
926
+ return 0;
927
+ }
928
+
929
+ int Client::handle_expiry() {
930
+ auto now = util::timestamp(loop_);
931
+ if (auto rv = ngtcp2_conn_handle_expiry(conn_, now); rv != 0) {
932
+ std::cerr << "ngtcp2_conn_handle_expiry: " << ngtcp2_strerror(rv)
933
+ << std::endl;
934
+ ngtcp2_connection_close_error_set_transport_error_liberr(&last_error_, rv,
935
+ nullptr, 0);
936
+ disconnect();
937
+ return -1;
938
+ }
939
+
940
+ return 0;
941
+ }
942
+
943
+ int Client::on_write() {
944
+ if (tx_.send_blocked) {
945
+ if (auto rv = send_blocked_packet(); rv != 0) {
946
+ return rv;
947
+ }
948
+
949
+ if (tx_.send_blocked) {
950
+ return 0;
951
+ }
952
+
953
+ ev_io_stop(loop_, &wev_);
954
+ }
955
+
956
+ if (auto rv = write_streams(); rv != 0) {
957
+ return rv;
958
+ }
959
+
960
+ if (should_exit_) {
961
+ ngtcp2_connection_close_error_set_application_error(
962
+ &last_error_, nghttp3_err_infer_quic_app_error_code(0), nullptr, 0);
963
+ disconnect();
964
+ return -1;
965
+ }
966
+
967
+ update_timer();
968
+ return 0;
969
+ }
970
+
971
+ int Client::write_streams() {
972
+ std::array<nghttp3_vec, 16> vec;
973
+ ngtcp2_path_storage ps;
974
+ size_t pktcnt = 0;
975
+ auto max_udp_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(conn_);
976
+ auto max_pktcnt = ngtcp2_conn_get_send_quantum(conn_) / max_udp_payload_size;
977
+ auto ts = util::timestamp(loop_);
978
+
979
+ ngtcp2_path_storage_zero(&ps);
980
+
981
+ for (;;) {
982
+ int64_t stream_id = -1;
983
+ int fin = 0;
984
+ nghttp3_ssize sveccnt = 0;
985
+
986
+ if (httpconn_ && ngtcp2_conn_get_max_data_left(conn_)) {
987
+ sveccnt = nghttp3_conn_writev_stream(httpconn_, &stream_id, &fin,
988
+ vec.data(), vec.size());
989
+ if (sveccnt < 0) {
990
+ std::cerr << "nghttp3_conn_writev_stream: " << nghttp3_strerror(sveccnt)
991
+ << std::endl;
992
+ ngtcp2_connection_close_error_set_application_error(
993
+ &last_error_, nghttp3_err_infer_quic_app_error_code(sveccnt),
994
+ nullptr, 0);
995
+ disconnect();
996
+ return -1;
997
+ }
998
+ }
999
+
1000
+ ngtcp2_ssize ndatalen;
1001
+ auto v = vec.data();
1002
+ auto vcnt = static_cast<size_t>(sveccnt);
1003
+
1004
+ uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_MORE;
1005
+ if (fin) {
1006
+ flags |= NGTCP2_WRITE_STREAM_FLAG_FIN;
1007
+ }
1008
+
1009
+ ngtcp2_pkt_info pi;
1010
+
1011
+ auto nwrite = ngtcp2_conn_writev_stream(
1012
+ conn_, &ps.path, &pi, tx_.data.data(), max_udp_payload_size, &ndatalen,
1013
+ flags, stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt, ts);
1014
+ if (nwrite < 0) {
1015
+ switch (nwrite) {
1016
+ case NGTCP2_ERR_STREAM_DATA_BLOCKED:
1017
+ assert(ndatalen == -1);
1018
+ nghttp3_conn_block_stream(httpconn_, stream_id);
1019
+ continue;
1020
+ case NGTCP2_ERR_STREAM_SHUT_WR:
1021
+ assert(ndatalen == -1);
1022
+ nghttp3_conn_shutdown_stream_write(httpconn_, stream_id);
1023
+ continue;
1024
+ case NGTCP2_ERR_WRITE_MORE:
1025
+ assert(ndatalen >= 0);
1026
+ if (auto rv =
1027
+ nghttp3_conn_add_write_offset(httpconn_, stream_id, ndatalen);
1028
+ rv != 0) {
1029
+ std::cerr << "nghttp3_conn_add_write_offset: " << nghttp3_strerror(rv)
1030
+ << std::endl;
1031
+ ngtcp2_connection_close_error_set_application_error(
1032
+ &last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr,
1033
+ 0);
1034
+ disconnect();
1035
+ return -1;
1036
+ }
1037
+ continue;
1038
+ }
1039
+
1040
+ assert(ndatalen == -1);
1041
+
1042
+ std::cerr << "ngtcp2_conn_write_stream: " << ngtcp2_strerror(nwrite)
1043
+ << std::endl;
1044
+ ngtcp2_connection_close_error_set_transport_error_liberr(
1045
+ &last_error_, nwrite, nullptr, 0);
1046
+ disconnect();
1047
+ return -1;
1048
+ } else if (ndatalen >= 0) {
1049
+ if (auto rv =
1050
+ nghttp3_conn_add_write_offset(httpconn_, stream_id, ndatalen);
1051
+ rv != 0) {
1052
+ std::cerr << "nghttp3_conn_add_write_offset: " << nghttp3_strerror(rv)
1053
+ << std::endl;
1054
+ ngtcp2_connection_close_error_set_application_error(
1055
+ &last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr,
1056
+ 0);
1057
+ disconnect();
1058
+ return -1;
1059
+ }
1060
+ }
1061
+
1062
+ if (nwrite == 0) {
1063
+ // We are congestion limited.
1064
+ ngtcp2_conn_update_pkt_tx_time(conn_, ts);
1065
+ return 0;
1066
+ }
1067
+
1068
+ auto &ep = *static_cast<Endpoint *>(ps.path.user_data);
1069
+
1070
+ if (auto rv =
1071
+ send_packet(ep, ps.path.remote, pi.ecn, tx_.data.data(), nwrite);
1072
+ rv != NETWORK_ERR_OK) {
1073
+ if (rv != NETWORK_ERR_SEND_BLOCKED) {
1074
+ ngtcp2_connection_close_error_set_transport_error_liberr(
1075
+ &last_error_, NGTCP2_ERR_INTERNAL, nullptr, 0);
1076
+ disconnect();
1077
+
1078
+ return rv;
1079
+ }
1080
+
1081
+ ngtcp2_conn_update_pkt_tx_time(conn_, ts);
1082
+ on_send_blocked(ep, ps.path.remote, pi.ecn, nwrite);
1083
+
1084
+ return 0;
1085
+ }
1086
+
1087
+ if (++pktcnt == max_pktcnt) {
1088
+ ngtcp2_conn_update_pkt_tx_time(conn_, ts);
1089
+ return 0;
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ void Client::update_timer() {
1095
+ auto expiry = ngtcp2_conn_get_expiry(conn_);
1096
+ auto now = util::timestamp(loop_);
1097
+
1098
+ if (expiry <= now) {
1099
+ if (!config.quiet) {
1100
+ auto t = static_cast<ev_tstamp>(now - expiry) / NGTCP2_SECONDS;
1101
+ std::cerr << "Timer has already expired: " << std::fixed << t << "s"
1102
+ << std::defaultfloat << std::endl;
1103
+ }
1104
+
1105
+ ev_feed_event(loop_, &timer_, EV_TIMER);
1106
+
1107
+ return;
1108
+ }
1109
+
1110
+ auto t = static_cast<ev_tstamp>(expiry - now) / NGTCP2_SECONDS;
1111
+ if (!config.quiet) {
1112
+ std::cerr << "Set timer=" << std::fixed << t << "s" << std::defaultfloat
1113
+ << std::endl;
1114
+ }
1115
+ timer_.repeat = t;
1116
+ ev_timer_again(loop_, &timer_);
1117
+ }
1118
+
1119
+ #ifdef HAVE_LINUX_RTNETLINK_H
1120
+ namespace {
1121
+ int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
1122
+ int family) {
1123
+ addrinfo hints{};
1124
+ addrinfo *res, *rp;
1125
+
1126
+ hints.ai_family = family;
1127
+ hints.ai_socktype = SOCK_DGRAM;
1128
+ hints.ai_flags = AI_PASSIVE;
1129
+
1130
+ char *node;
1131
+ std::array<char, NI_MAXHOST> nodebuf;
1132
+
1133
+ if (iau) {
1134
+ if (inet_ntop(family, iau, nodebuf.data(), nodebuf.size()) == nullptr) {
1135
+ std::cerr << "inet_ntop: " << strerror(errno) << std::endl;
1136
+ return -1;
1137
+ }
1138
+
1139
+ node = nodebuf.data();
1140
+ } else {
1141
+ node = nullptr;
1142
+ }
1143
+
1144
+ if (auto rv = getaddrinfo(node, "0", &hints, &res); rv != 0) {
1145
+ std::cerr << "getaddrinfo: " << gai_strerror(rv) << std::endl;
1146
+ return -1;
1147
+ }
1148
+
1149
+ auto res_d = defer(freeaddrinfo, res);
1150
+
1151
+ for (rp = res; rp; rp = rp->ai_next) {
1152
+ if (bind(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
1153
+ break;
1154
+ }
1155
+ }
1156
+
1157
+ if (!rp) {
1158
+ std::cerr << "Could not bind" << std::endl;
1159
+ return -1;
1160
+ }
1161
+
1162
+ socklen_t len = sizeof(local_addr.su.storage);
1163
+ if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
1164
+ std::cerr << "getsockname: " << strerror(errno) << std::endl;
1165
+ return -1;
1166
+ }
1167
+ local_addr.len = len;
1168
+ local_addr.ifindex = 0;
1169
+
1170
+ return 0;
1171
+ }
1172
+ } // namespace
1173
+ #endif // HAVE_LINUX_RTNETLINK_H
1174
+
1175
+ #ifndef HAVE_LINUX_RTNETLINK_H
1176
+ namespace {
1177
+ int connect_sock(Address &local_addr, int fd, const Address &remote_addr) {
1178
+ if (connect(fd, &remote_addr.su.sa, remote_addr.len) != 0) {
1179
+ std::cerr << "connect: " << strerror(errno) << std::endl;
1180
+ return -1;
1181
+ }
1182
+
1183
+ socklen_t len = sizeof(local_addr.su.storage);
1184
+ if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
1185
+ std::cerr << "getsockname: " << strerror(errno) << std::endl;
1186
+ return -1;
1187
+ }
1188
+ local_addr.len = len;
1189
+ local_addr.ifindex = 0;
1190
+
1191
+ return 0;
1192
+ }
1193
+ } // namespace
1194
+ #endif // !HAVE_LINUX_RTNETLINK_H
1195
+
1196
+ namespace {
1197
+ int udp_sock(int family) {
1198
+ auto fd = util::create_nonblock_socket(family, SOCK_DGRAM, IPPROTO_UDP);
1199
+ if (fd == -1) {
1200
+ return -1;
1201
+ }
1202
+
1203
+ fd_set_recv_ecn(fd, family);
1204
+ fd_set_ip_mtu_discover(fd, family);
1205
+ fd_set_ip_dontfrag(fd, family);
1206
+
1207
+ return fd;
1208
+ }
1209
+ } // namespace
1210
+
1211
+ namespace {
1212
+ int create_sock(Address &remote_addr, const char *addr, const char *port) {
1213
+ addrinfo hints{};
1214
+ addrinfo *res, *rp;
1215
+
1216
+ hints.ai_family = AF_UNSPEC;
1217
+ hints.ai_socktype = SOCK_DGRAM;
1218
+
1219
+ if (auto rv = getaddrinfo(addr, port, &hints, &res); rv != 0) {
1220
+ std::cerr << "getaddrinfo: " << gai_strerror(rv) << std::endl;
1221
+ return -1;
1222
+ }
1223
+
1224
+ auto res_d = defer(freeaddrinfo, res);
1225
+
1226
+ int fd = -1;
1227
+
1228
+ for (rp = res; rp; rp = rp->ai_next) {
1229
+ fd = udp_sock(rp->ai_family);
1230
+ if (fd == -1) {
1231
+ continue;
1232
+ }
1233
+
1234
+ break;
1235
+ }
1236
+
1237
+ if (!rp) {
1238
+ std::cerr << "Could not create socket" << std::endl;
1239
+ return -1;
1240
+ }
1241
+
1242
+ remote_addr.len = rp->ai_addrlen;
1243
+ memcpy(&remote_addr.su, rp->ai_addr, rp->ai_addrlen);
1244
+
1245
+ return fd;
1246
+ }
1247
+ } // namespace
1248
+
1249
+ std::optional<Endpoint *> Client::endpoint_for(const Address &remote_addr) {
1250
+ #ifdef HAVE_LINUX_RTNETLINK_H
1251
+ in_addr_union iau;
1252
+
1253
+ if (get_local_addr(iau, remote_addr) != 0) {
1254
+ std::cerr << "Could not get local address for a selected preferred address"
1255
+ << std::endl;
1256
+ return nullptr;
1257
+ }
1258
+
1259
+ auto current_path = ngtcp2_conn_get_path(conn_);
1260
+ auto current_ep = static_cast<Endpoint *>(current_path->user_data);
1261
+ if (addreq(&current_ep->addr.su.sa, iau)) {
1262
+ return current_ep;
1263
+ }
1264
+ #endif // HAVE_LINUX_RTNETLINK_H
1265
+
1266
+ auto fd = udp_sock(remote_addr.su.sa.sa_family);
1267
+ if (fd == -1) {
1268
+ return nullptr;
1269
+ }
1270
+
1271
+ Address local_addr;
1272
+
1273
+ #ifdef HAVE_LINUX_RTNETLINK_H
1274
+ if (bind_addr(local_addr, fd, &iau, remote_addr.su.sa.sa_family) != 0) {
1275
+ close(fd);
1276
+ return nullptr;
1277
+ }
1278
+ #else // !HAVE_LINUX_RTNETLINK_H
1279
+ if (connect_sock(local_addr, fd, remote_addr) != 0) {
1280
+ close(fd);
1281
+ return nullptr;
1282
+ }
1283
+ #endif // !HAVE_LINUX_RTNETLINK_H
1284
+
1285
+ endpoints_.emplace_back();
1286
+ auto &ep = endpoints_.back();
1287
+ ep.addr = local_addr;
1288
+ ep.client = this;
1289
+ ep.fd = fd;
1290
+ ev_io_init(&ep.rev, readcb, fd, EV_READ);
1291
+ ep.rev.data = &ep;
1292
+
1293
+ ev_io_start(loop_, &ep.rev);
1294
+
1295
+ return &ep;
1296
+ }
1297
+
1298
+ void Client::start_change_local_addr_timer() {
1299
+ ev_timer_start(loop_, &change_local_addr_timer_);
1300
+ }
1301
+
1302
+ int Client::change_local_addr() {
1303
+ Address local_addr;
1304
+
1305
+ if (!config.quiet) {
1306
+ std::cerr << "Changing local address" << std::endl;
1307
+ }
1308
+
1309
+ auto nfd = udp_sock(remote_addr_.su.sa.sa_family);
1310
+ if (nfd == -1) {
1311
+ return -1;
1312
+ }
1313
+
1314
+ #ifdef HAVE_LINUX_RTNETLINK_H
1315
+ in_addr_union iau;
1316
+
1317
+ if (get_local_addr(iau, remote_addr_) != 0) {
1318
+ std::cerr << "Could not get local address" << std::endl;
1319
+ close(nfd);
1320
+ return -1;
1321
+ }
1322
+
1323
+ if (bind_addr(local_addr, nfd, &iau, remote_addr_.su.sa.sa_family) != 0) {
1324
+ close(nfd);
1325
+ return -1;
1326
+ }
1327
+ #else // !HAVE_LINUX_RTNETLINK_H
1328
+ if (connect_sock(local_addr, nfd, remote_addr_) != 0) {
1329
+ close(nfd);
1330
+ return -1;
1331
+ }
1332
+ #endif // !HAVE_LINUX_RTNETLINK_H
1333
+
1334
+ if (!config.quiet) {
1335
+ std::cerr << "Local address is now "
1336
+ << util::straddr(&local_addr.su.sa, local_addr.len) << std::endl;
1337
+ }
1338
+
1339
+ endpoints_.emplace_back();
1340
+ auto &ep = endpoints_.back();
1341
+ ep.addr = local_addr;
1342
+ ep.client = this;
1343
+ ep.fd = nfd;
1344
+ ev_io_init(&ep.rev, readcb, nfd, EV_READ);
1345
+ ep.rev.data = &ep;
1346
+
1347
+ ngtcp2_addr addr;
1348
+ ngtcp2_addr_init(&addr, &local_addr.su.sa, local_addr.len);
1349
+
1350
+ if (config.nat_rebinding) {
1351
+ ngtcp2_conn_set_local_addr(conn_, &addr);
1352
+ ngtcp2_conn_set_path_user_data(conn_, &ep);
1353
+ } else {
1354
+ auto path = ngtcp2_path{
1355
+ addr,
1356
+ {
1357
+ const_cast<sockaddr *>(&remote_addr_.su.sa),
1358
+ remote_addr_.len,
1359
+ },
1360
+ &ep,
1361
+ };
1362
+ if (auto rv = ngtcp2_conn_initiate_immediate_migration(
1363
+ conn_, &path, util::timestamp(loop_));
1364
+ rv != 0) {
1365
+ std::cerr << "ngtcp2_conn_initiate_immediate_migration: "
1366
+ << ngtcp2_strerror(rv) << std::endl;
1367
+ }
1368
+ }
1369
+
1370
+ ev_io_start(loop_, &ep.rev);
1371
+
1372
+ return 0;
1373
+ }
1374
+
1375
+ void Client::start_key_update_timer() {
1376
+ ev_timer_start(loop_, &key_update_timer_);
1377
+ }
1378
+
1379
+ int Client::update_key(uint8_t *rx_secret, uint8_t *tx_secret,
1380
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
1381
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
1382
+ const uint8_t *current_rx_secret,
1383
+ const uint8_t *current_tx_secret, size_t secretlen) {
1384
+ if (!config.quiet) {
1385
+ std::cerr << "Updating traffic key" << std::endl;
1386
+ }
1387
+
1388
+ auto crypto_ctx = ngtcp2_conn_get_crypto_ctx(conn_);
1389
+ auto aead = &crypto_ctx->aead;
1390
+ auto keylen = ngtcp2_crypto_aead_keylen(aead);
1391
+ auto ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
1392
+
1393
+ ++nkey_update_;
1394
+
1395
+ std::array<uint8_t, 64> rx_key, tx_key;
1396
+
1397
+ if (ngtcp2_crypto_update_key(conn_, rx_secret, tx_secret, rx_aead_ctx,
1398
+ rx_key.data(), rx_iv, tx_aead_ctx, tx_key.data(),
1399
+ tx_iv, current_rx_secret, current_tx_secret,
1400
+ secretlen) != 0) {
1401
+ return -1;
1402
+ }
1403
+
1404
+ if (!config.quiet && config.show_secret) {
1405
+ std::cerr << "application_traffic rx secret " << nkey_update_ << std::endl;
1406
+ debug::print_secrets(rx_secret, secretlen, rx_key.data(), keylen, rx_iv,
1407
+ ivlen);
1408
+ std::cerr << "application_traffic tx secret " << nkey_update_ << std::endl;
1409
+ debug::print_secrets(tx_secret, secretlen, tx_key.data(), keylen, tx_iv,
1410
+ ivlen);
1411
+ }
1412
+
1413
+ return 0;
1414
+ }
1415
+
1416
+ int Client::initiate_key_update() {
1417
+ if (!config.quiet) {
1418
+ std::cerr << "Initiate key update" << std::endl;
1419
+ }
1420
+
1421
+ if (auto rv = ngtcp2_conn_initiate_key_update(conn_, util::timestamp(loop_));
1422
+ rv != 0) {
1423
+ std::cerr << "ngtcp2_conn_initiate_key_update: " << ngtcp2_strerror(rv)
1424
+ << std::endl;
1425
+ return -1;
1426
+ }
1427
+
1428
+ return 0;
1429
+ }
1430
+
1431
+ void Client::start_delay_stream_timer() {
1432
+ ev_timer_start(loop_, &delay_stream_timer_);
1433
+ }
1434
+
1435
+ int Client::send_packet(const Endpoint &ep, const ngtcp2_addr &remote_addr,
1436
+ unsigned int ecn, const uint8_t *data, size_t datalen) {
1437
+ if (debug::packet_lost(config.tx_loss_prob)) {
1438
+ if (!config.quiet) {
1439
+ std::cerr << "** Simulated outgoing packet loss **" << std::endl;
1440
+ }
1441
+ return NETWORK_ERR_OK;
1442
+ }
1443
+
1444
+ iovec msg_iov;
1445
+ msg_iov.iov_base = const_cast<uint8_t *>(data);
1446
+ msg_iov.iov_len = datalen;
1447
+
1448
+ msghdr msg{};
1449
+ #ifdef HAVE_LINUX_RTNETLINK_H
1450
+ msg.msg_name = const_cast<sockaddr *>(remote_addr.addr);
1451
+ msg.msg_namelen = remote_addr.addrlen;
1452
+ #endif // HAVE_LINUX_RTNETLINK_H
1453
+ msg.msg_iov = &msg_iov;
1454
+ msg.msg_iovlen = 1;
1455
+
1456
+ fd_set_ecn(ep.fd, remote_addr.addr->sa_family, ecn);
1457
+
1458
+ ssize_t nwrite = 0;
1459
+
1460
+ do {
1461
+ nwrite = sendmsg(ep.fd, &msg, 0);
1462
+ } while (nwrite == -1 && errno == EINTR);
1463
+
1464
+ if (nwrite == -1) {
1465
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
1466
+ return NETWORK_ERR_SEND_BLOCKED;
1467
+ }
1468
+ std::cerr << "sendmsg: " << strerror(errno) << std::endl;
1469
+ if (errno == EMSGSIZE) {
1470
+ return 0;
1471
+ }
1472
+ return NETWORK_ERR_FATAL;
1473
+ }
1474
+
1475
+ assert(static_cast<size_t>(nwrite) == datalen);
1476
+
1477
+ if (!config.quiet) {
1478
+ std::cerr << "Sent packet: local="
1479
+ << util::straddr(&ep.addr.su.sa, ep.addr.len) << " remote="
1480
+ << util::straddr(remote_addr.addr, remote_addr.addrlen)
1481
+ << " ecn=0x" << std::hex << ecn << std::dec << " " << nwrite
1482
+ << " bytes" << std::endl;
1483
+ }
1484
+
1485
+ return NETWORK_ERR_OK;
1486
+ }
1487
+
1488
+ void Client::on_send_blocked(const Endpoint &ep, const ngtcp2_addr &remote_addr,
1489
+ unsigned int ecn, size_t datalen) {
1490
+ assert(!tx_.send_blocked);
1491
+
1492
+ tx_.send_blocked = true;
1493
+
1494
+ memcpy(&tx_.blocked.remote_addr.su, remote_addr.addr, remote_addr.addrlen);
1495
+ tx_.blocked.remote_addr.len = remote_addr.addrlen;
1496
+ tx_.blocked.ecn = ecn;
1497
+ tx_.blocked.datalen = datalen;
1498
+ tx_.blocked.endpoint = &ep;
1499
+
1500
+ start_wev_endpoint(ep);
1501
+ }
1502
+
1503
+ void Client::start_wev_endpoint(const Endpoint &ep) {
1504
+ // We do not close ep.fd, so we can expect that each Endpoint has
1505
+ // unique fd.
1506
+ if (ep.fd != wev_.fd) {
1507
+ if (ev_is_active(&wev_)) {
1508
+ ev_io_stop(loop_, &wev_);
1509
+ }
1510
+
1511
+ ev_io_set(&wev_, ep.fd, EV_WRITE);
1512
+ }
1513
+
1514
+ ev_io_start(loop_, &wev_);
1515
+ }
1516
+
1517
+ int Client::send_blocked_packet() {
1518
+ assert(tx_.send_blocked);
1519
+
1520
+ ngtcp2_addr remote_addr{
1521
+ .addr = &tx_.blocked.remote_addr.su.sa,
1522
+ .addrlen = tx_.blocked.remote_addr.len,
1523
+ };
1524
+
1525
+ auto rv = send_packet(*tx_.blocked.endpoint, remote_addr, tx_.blocked.ecn,
1526
+ tx_.data.data(), tx_.blocked.datalen);
1527
+ if (rv != 0) {
1528
+ if (rv == NETWORK_ERR_SEND_BLOCKED) {
1529
+ assert(wev_.fd == tx_.blocked.endpoint->fd);
1530
+
1531
+ return 0;
1532
+ }
1533
+
1534
+ ngtcp2_connection_close_error_set_transport_error_liberr(
1535
+ &last_error_, NGTCP2_ERR_INTERNAL, nullptr, 0);
1536
+ disconnect();
1537
+
1538
+ return rv;
1539
+ }
1540
+
1541
+ tx_.send_blocked = false;
1542
+
1543
+ return 0;
1544
+ }
1545
+
1546
+ int Client::handle_error() {
1547
+ if (!conn_ || ngtcp2_conn_is_in_closing_period(conn_) ||
1548
+ ngtcp2_conn_is_in_draining_period(conn_)) {
1549
+ return 0;
1550
+ }
1551
+
1552
+ std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
1553
+
1554
+ ngtcp2_path_storage ps;
1555
+
1556
+ ngtcp2_path_storage_zero(&ps);
1557
+
1558
+ ngtcp2_pkt_info pi;
1559
+
1560
+ auto nwrite = ngtcp2_conn_write_connection_close(
1561
+ conn_, &ps.path, &pi, buf.data(), buf.size(), &last_error_,
1562
+ util::timestamp(loop_));
1563
+ if (nwrite < 0) {
1564
+ std::cerr << "ngtcp2_conn_write_connection_close: "
1565
+ << ngtcp2_strerror(nwrite) << std::endl;
1566
+ return -1;
1567
+ }
1568
+
1569
+ if (nwrite == 0) {
1570
+ return 0;
1571
+ }
1572
+
1573
+ return send_packet(*static_cast<Endpoint *>(ps.path.user_data),
1574
+ ps.path.remote, pi.ecn, buf.data(), nwrite);
1575
+ }
1576
+
1577
+ int Client::on_stream_close(int64_t stream_id, uint64_t app_error_code) {
1578
+ if (httpconn_) {
1579
+ if (app_error_code == 0) {
1580
+ app_error_code = NGHTTP3_H3_NO_ERROR;
1581
+ }
1582
+ auto rv = nghttp3_conn_close_stream(httpconn_, stream_id, app_error_code);
1583
+ switch (rv) {
1584
+ case 0:
1585
+ break;
1586
+ case NGHTTP3_ERR_STREAM_NOT_FOUND:
1587
+ // We have to handle the case when stream opened but no data is
1588
+ // transferred. In this case, nghttp3_conn_close_stream might
1589
+ // return error.
1590
+ if (!ngtcp2_is_bidi_stream(stream_id)) {
1591
+ assert(!ngtcp2_conn_is_local_stream(conn_, stream_id));
1592
+ ngtcp2_conn_extend_max_streams_uni(conn_, 1);
1593
+ }
1594
+ break;
1595
+ default:
1596
+ std::cerr << "nghttp3_conn_close_stream: " << nghttp3_strerror(rv)
1597
+ << std::endl;
1598
+ ngtcp2_connection_close_error_set_application_error(
1599
+ &last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr, 0);
1600
+ return -1;
1601
+ }
1602
+ }
1603
+
1604
+ return 0;
1605
+ }
1606
+
1607
+ int Client::on_stream_reset(int64_t stream_id) {
1608
+ if (httpconn_) {
1609
+ if (auto rv = nghttp3_conn_shutdown_stream_read(httpconn_, stream_id);
1610
+ rv != 0) {
1611
+ std::cerr << "nghttp3_conn_shutdown_stream_read: " << nghttp3_strerror(rv)
1612
+ << std::endl;
1613
+ return -1;
1614
+ }
1615
+ }
1616
+ return 0;
1617
+ }
1618
+
1619
+ int Client::on_stream_stop_sending(int64_t stream_id) {
1620
+ if (!httpconn_) {
1621
+ return 0;
1622
+ }
1623
+
1624
+ if (auto rv = nghttp3_conn_shutdown_stream_read(httpconn_, stream_id);
1625
+ rv != 0) {
1626
+ std::cerr << "nghttp3_conn_shutdown_stream_read: " << nghttp3_strerror(rv)
1627
+ << std::endl;
1628
+ return -1;
1629
+ }
1630
+
1631
+ return 0;
1632
+ }
1633
+
1634
+ int Client::make_stream_early() {
1635
+ if (setup_httpconn() != 0) {
1636
+ return -1;
1637
+ }
1638
+
1639
+ return on_extend_max_streams();
1640
+ }
1641
+
1642
+ int Client::on_extend_max_streams() {
1643
+ int64_t stream_id;
1644
+
1645
+ if ((config.delay_stream && !handshake_confirmed_) ||
1646
+ ev_is_active(&delay_stream_timer_)) {
1647
+ return 0;
1648
+ }
1649
+
1650
+ for (; nstreams_done_ < config.nstreams; ++nstreams_done_) {
1651
+ if (auto rv = ngtcp2_conn_open_bidi_stream(conn_, &stream_id, nullptr);
1652
+ rv != 0) {
1653
+ assert(NGTCP2_ERR_STREAM_ID_BLOCKED == rv);
1654
+ break;
1655
+ }
1656
+
1657
+ auto stream = std::make_unique<Stream>(
1658
+ config.requests[nstreams_done_ % config.requests.size()], stream_id);
1659
+
1660
+ if (submit_http_request(stream.get()) != 0) {
1661
+ break;
1662
+ }
1663
+
1664
+ if (!config.download.empty()) {
1665
+ stream->open_file(stream->req.path);
1666
+ }
1667
+ streams_.emplace(stream_id, std::move(stream));
1668
+ }
1669
+ return 0;
1670
+ }
1671
+
1672
+ namespace {
1673
+ nghttp3_ssize read_data(nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec,
1674
+ size_t veccnt, uint32_t *pflags, void *user_data,
1675
+ void *stream_user_data) {
1676
+ vec[0].base = config.data;
1677
+ vec[0].len = config.datalen;
1678
+ *pflags |= NGHTTP3_DATA_FLAG_EOF;
1679
+
1680
+ return 1;
1681
+ }
1682
+ } // namespace
1683
+
1684
+ int Client::submit_http_request(const Stream *stream) {
1685
+ std::string content_length_str;
1686
+
1687
+ const auto &req = stream->req;
1688
+
1689
+ std::array<nghttp3_nv, 6> nva{
1690
+ util::make_nv_nn(":method", config.http_method),
1691
+ util::make_nv_nn(":scheme", req.scheme),
1692
+ util::make_nv_nn(":authority", req.authority),
1693
+ util::make_nv_nn(":path", req.path),
1694
+ util::make_nv_nn("user-agent", "nghttp3/ngtcp2 client"),
1695
+ };
1696
+ size_t nvlen = 5;
1697
+ if (config.fd != -1) {
1698
+ content_length_str = util::format_uint(config.datalen);
1699
+ nva[nvlen++] = util::make_nv_nc("content-length", content_length_str);
1700
+ }
1701
+
1702
+ if (!config.quiet) {
1703
+ debug::print_http_request_headers(stream->stream_id, nva.data(), nvlen);
1704
+ }
1705
+
1706
+ nghttp3_data_reader dr{};
1707
+ dr.read_data = read_data;
1708
+
1709
+ if (auto rv = nghttp3_conn_submit_request(
1710
+ httpconn_, stream->stream_id, nva.data(), nvlen,
1711
+ config.fd == -1 ? nullptr : &dr, nullptr);
1712
+ rv != 0) {
1713
+ std::cerr << "nghttp3_conn_submit_request: " << nghttp3_strerror(rv)
1714
+ << std::endl;
1715
+ return -1;
1716
+ }
1717
+
1718
+ return 0;
1719
+ }
1720
+
1721
+ int Client::recv_stream_data(uint32_t flags, int64_t stream_id,
1722
+ const uint8_t *data, size_t datalen) {
1723
+ auto nconsumed = nghttp3_conn_read_stream(
1724
+ httpconn_, stream_id, data, datalen, flags & NGTCP2_STREAM_DATA_FLAG_FIN);
1725
+ if (nconsumed < 0) {
1726
+ std::cerr << "nghttp3_conn_read_stream: " << nghttp3_strerror(nconsumed)
1727
+ << std::endl;
1728
+ ngtcp2_connection_close_error_set_application_error(
1729
+ &last_error_, nghttp3_err_infer_quic_app_error_code(nconsumed), nullptr,
1730
+ 0);
1731
+ return -1;
1732
+ }
1733
+
1734
+ ngtcp2_conn_extend_max_stream_offset(conn_, stream_id, nconsumed);
1735
+ ngtcp2_conn_extend_max_offset(conn_, nconsumed);
1736
+
1737
+ return 0;
1738
+ }
1739
+
1740
+ int Client::acked_stream_data_offset(int64_t stream_id, uint64_t datalen) {
1741
+ if (auto rv = nghttp3_conn_add_ack_offset(httpconn_, stream_id, datalen);
1742
+ rv != 0) {
1743
+ std::cerr << "nghttp3_conn_add_ack_offset: " << nghttp3_strerror(rv)
1744
+ << std::endl;
1745
+ return -1;
1746
+ }
1747
+
1748
+ return 0;
1749
+ }
1750
+
1751
+ int Client::select_preferred_address(Address &selected_addr,
1752
+ const ngtcp2_preferred_addr *paddr) {
1753
+ auto path = ngtcp2_conn_get_path(conn_);
1754
+
1755
+ switch (path->local.addr->sa_family) {
1756
+ case AF_INET:
1757
+ if (!paddr->ipv4_present) {
1758
+ return -1;
1759
+ }
1760
+ selected_addr.su.in = paddr->ipv4;
1761
+ selected_addr.len = sizeof(paddr->ipv4);
1762
+ break;
1763
+ case AF_INET6:
1764
+ if (!paddr->ipv6_present) {
1765
+ return -1;
1766
+ }
1767
+ selected_addr.su.in6 = paddr->ipv6;
1768
+ selected_addr.len = sizeof(paddr->ipv6);
1769
+ break;
1770
+ default:
1771
+ return -1;
1772
+ }
1773
+
1774
+ char host[NI_MAXHOST], service[NI_MAXSERV];
1775
+ if (auto rv = getnameinfo(&selected_addr.su.sa, selected_addr.len, host,
1776
+ sizeof(host), service, sizeof(service),
1777
+ NI_NUMERICHOST | NI_NUMERICSERV);
1778
+ rv != 0) {
1779
+ std::cerr << "getnameinfo: " << gai_strerror(rv) << std::endl;
1780
+ return -1;
1781
+ }
1782
+
1783
+ if (!config.quiet) {
1784
+ std::cerr << "selected server preferred_address is [" << host
1785
+ << "]:" << service << std::endl;
1786
+ }
1787
+
1788
+ return 0;
1789
+ }
1790
+
1791
+ namespace {
1792
+ int http_recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data,
1793
+ size_t datalen, void *user_data, void *stream_user_data) {
1794
+ if (!config.quiet && !config.no_http_dump) {
1795
+ debug::print_http_data(stream_id, data, datalen);
1796
+ }
1797
+ auto c = static_cast<Client *>(user_data);
1798
+ c->http_consume(stream_id, datalen);
1799
+ c->http_write_data(stream_id, data, datalen);
1800
+ return 0;
1801
+ }
1802
+ } // namespace
1803
+
1804
+ namespace {
1805
+ int http_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
1806
+ size_t nconsumed, void *user_data,
1807
+ void *stream_user_data) {
1808
+ auto c = static_cast<Client *>(user_data);
1809
+ c->http_consume(stream_id, nconsumed);
1810
+ return 0;
1811
+ }
1812
+ } // namespace
1813
+
1814
+ void Client::http_consume(int64_t stream_id, size_t nconsumed) {
1815
+ ngtcp2_conn_extend_max_stream_offset(conn_, stream_id, nconsumed);
1816
+ ngtcp2_conn_extend_max_offset(conn_, nconsumed);
1817
+ }
1818
+
1819
+ void Client::http_write_data(int64_t stream_id, const uint8_t *data,
1820
+ size_t datalen) {
1821
+ auto it = streams_.find(stream_id);
1822
+ if (it == std::end(streams_)) {
1823
+ return;
1824
+ }
1825
+
1826
+ auto &stream = (*it).second;
1827
+
1828
+ if (stream->fd == -1) {
1829
+ return;
1830
+ }
1831
+
1832
+ ssize_t nwrite;
1833
+ do {
1834
+ nwrite = write(stream->fd, data, datalen);
1835
+ } while (nwrite == -1 && errno == EINTR);
1836
+ }
1837
+
1838
+ namespace {
1839
+ int http_begin_headers(nghttp3_conn *conn, int64_t stream_id, void *user_data,
1840
+ void *stream_user_data) {
1841
+ if (!config.quiet) {
1842
+ debug::print_http_begin_response_headers(stream_id);
1843
+ }
1844
+ return 0;
1845
+ }
1846
+ } // namespace
1847
+
1848
+ namespace {
1849
+ int http_recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token,
1850
+ nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags,
1851
+ void *user_data, void *stream_user_data) {
1852
+ if (!config.quiet) {
1853
+ debug::print_http_header(stream_id, name, value, flags);
1854
+ }
1855
+ return 0;
1856
+ }
1857
+ } // namespace
1858
+
1859
+ namespace {
1860
+ int http_end_headers(nghttp3_conn *conn, int64_t stream_id, int fin,
1861
+ void *user_data, void *stream_user_data) {
1862
+ if (!config.quiet) {
1863
+ debug::print_http_end_headers(stream_id);
1864
+ }
1865
+ return 0;
1866
+ }
1867
+ } // namespace
1868
+
1869
+ namespace {
1870
+ int http_begin_trailers(nghttp3_conn *conn, int64_t stream_id, void *user_data,
1871
+ void *stream_user_data) {
1872
+ if (!config.quiet) {
1873
+ debug::print_http_begin_trailers(stream_id);
1874
+ }
1875
+ return 0;
1876
+ }
1877
+ } // namespace
1878
+
1879
+ namespace {
1880
+ int http_recv_trailer(nghttp3_conn *conn, int64_t stream_id, int32_t token,
1881
+ nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags,
1882
+ void *user_data, void *stream_user_data) {
1883
+ if (!config.quiet) {
1884
+ debug::print_http_header(stream_id, name, value, flags);
1885
+ }
1886
+ return 0;
1887
+ }
1888
+ } // namespace
1889
+
1890
+ namespace {
1891
+ int http_end_trailers(nghttp3_conn *conn, int64_t stream_id, int fin,
1892
+ void *user_data, void *stream_user_data) {
1893
+ if (!config.quiet) {
1894
+ debug::print_http_end_trailers(stream_id);
1895
+ }
1896
+ return 0;
1897
+ }
1898
+ } // namespace
1899
+
1900
+ namespace {
1901
+ int http_stop_sending(nghttp3_conn *conn, int64_t stream_id,
1902
+ uint64_t app_error_code, void *user_data,
1903
+ void *stream_user_data) {
1904
+ auto c = static_cast<Client *>(user_data);
1905
+ if (c->stop_sending(stream_id, app_error_code) != 0) {
1906
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
1907
+ }
1908
+ return 0;
1909
+ }
1910
+ } // namespace
1911
+
1912
+ int Client::stop_sending(int64_t stream_id, uint64_t app_error_code) {
1913
+ if (auto rv =
1914
+ ngtcp2_conn_shutdown_stream_read(conn_, stream_id, app_error_code);
1915
+ rv != 0) {
1916
+ std::cerr << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv)
1917
+ << std::endl;
1918
+ return -1;
1919
+ }
1920
+ return 0;
1921
+ }
1922
+
1923
+ namespace {
1924
+ int http_reset_stream(nghttp3_conn *conn, int64_t stream_id,
1925
+ uint64_t app_error_code, void *user_data,
1926
+ void *stream_user_data) {
1927
+ auto c = static_cast<Client *>(user_data);
1928
+ if (c->reset_stream(stream_id, app_error_code) != 0) {
1929
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
1930
+ }
1931
+ return 0;
1932
+ }
1933
+ } // namespace
1934
+
1935
+ int Client::reset_stream(int64_t stream_id, uint64_t app_error_code) {
1936
+ if (auto rv =
1937
+ ngtcp2_conn_shutdown_stream_write(conn_, stream_id, app_error_code);
1938
+ rv != 0) {
1939
+ std::cerr << "ngtcp2_conn_shutdown_stream_write: " << ngtcp2_strerror(rv)
1940
+ << std::endl;
1941
+ return -1;
1942
+ }
1943
+ return 0;
1944
+ }
1945
+
1946
+ namespace {
1947
+ int http_stream_close(nghttp3_conn *conn, int64_t stream_id,
1948
+ uint64_t app_error_code, void *conn_user_data,
1949
+ void *stream_user_data) {
1950
+ auto c = static_cast<Client *>(conn_user_data);
1951
+ if (c->http_stream_close(stream_id, app_error_code) != 0) {
1952
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
1953
+ }
1954
+ return 0;
1955
+ }
1956
+ } // namespace
1957
+
1958
+ int Client::http_stream_close(int64_t stream_id, uint64_t app_error_code) {
1959
+ if (ngtcp2_is_bidi_stream(stream_id)) {
1960
+ assert(ngtcp2_conn_is_local_stream(conn_, stream_id));
1961
+
1962
+ ++nstreams_closed_;
1963
+
1964
+ if (config.exit_on_first_stream_close ||
1965
+ (config.exit_on_all_streams_close &&
1966
+ config.nstreams == nstreams_done_ &&
1967
+ nstreams_closed_ == nstreams_done_)) {
1968
+ if (handshake_confirmed_) {
1969
+ should_exit_ = true;
1970
+ } else {
1971
+ should_exit_on_handshake_confirmed_ = true;
1972
+ }
1973
+ }
1974
+ } else {
1975
+ assert(!ngtcp2_conn_is_local_stream(conn_, stream_id));
1976
+ ngtcp2_conn_extend_max_streams_uni(conn_, 1);
1977
+ }
1978
+
1979
+ if (auto it = streams_.find(stream_id); it != std::end(streams_)) {
1980
+ if (!config.quiet) {
1981
+ std::cerr << "HTTP stream " << stream_id << " closed with error code "
1982
+ << app_error_code << std::endl;
1983
+ }
1984
+ streams_.erase(it);
1985
+ }
1986
+
1987
+ return 0;
1988
+ }
1989
+
1990
+ int Client::setup_httpconn() {
1991
+ if (httpconn_) {
1992
+ return 0;
1993
+ }
1994
+
1995
+ if (ngtcp2_conn_get_max_local_streams_uni(conn_) < 3) {
1996
+ std::cerr << "peer does not allow at least 3 unidirectional streams."
1997
+ << std::endl;
1998
+ return -1;
1999
+ }
2000
+
2001
+ nghttp3_callbacks callbacks{
2002
+ nullptr, // acked_stream_data
2003
+ ::http_stream_close,
2004
+ ::http_recv_data,
2005
+ ::http_deferred_consume,
2006
+ ::http_begin_headers,
2007
+ ::http_recv_header,
2008
+ ::http_end_headers,
2009
+ ::http_begin_trailers,
2010
+ ::http_recv_trailer,
2011
+ ::http_end_trailers,
2012
+ ::http_stop_sending,
2013
+ nullptr, // end_stream
2014
+ ::http_reset_stream,
2015
+ nullptr, // shutdown
2016
+ };
2017
+ nghttp3_settings settings;
2018
+ nghttp3_settings_default(&settings);
2019
+ settings.qpack_max_dtable_capacity = 4_k;
2020
+ settings.qpack_blocked_streams = 100;
2021
+
2022
+ auto mem = nghttp3_mem_default();
2023
+
2024
+ if (auto rv =
2025
+ nghttp3_conn_client_new(&httpconn_, &callbacks, &settings, mem, this);
2026
+ rv != 0) {
2027
+ std::cerr << "nghttp3_conn_client_new: " << nghttp3_strerror(rv)
2028
+ << std::endl;
2029
+ return -1;
2030
+ }
2031
+
2032
+ int64_t ctrl_stream_id;
2033
+
2034
+ if (auto rv = ngtcp2_conn_open_uni_stream(conn_, &ctrl_stream_id, nullptr);
2035
+ rv != 0) {
2036
+ std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
2037
+ << std::endl;
2038
+ return -1;
2039
+ }
2040
+
2041
+ if (auto rv = nghttp3_conn_bind_control_stream(httpconn_, ctrl_stream_id);
2042
+ rv != 0) {
2043
+ std::cerr << "nghttp3_conn_bind_control_stream: " << nghttp3_strerror(rv)
2044
+ << std::endl;
2045
+ return -1;
2046
+ }
2047
+
2048
+ if (!config.quiet) {
2049
+ fprintf(stderr, "http: control stream=%" PRIx64 "\n", ctrl_stream_id);
2050
+ }
2051
+
2052
+ int64_t qpack_enc_stream_id, qpack_dec_stream_id;
2053
+
2054
+ if (auto rv =
2055
+ ngtcp2_conn_open_uni_stream(conn_, &qpack_enc_stream_id, nullptr);
2056
+ rv != 0) {
2057
+ std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
2058
+ << std::endl;
2059
+ return -1;
2060
+ }
2061
+
2062
+ if (auto rv =
2063
+ ngtcp2_conn_open_uni_stream(conn_, &qpack_dec_stream_id, nullptr);
2064
+ rv != 0) {
2065
+ std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
2066
+ << std::endl;
2067
+ return -1;
2068
+ }
2069
+
2070
+ if (auto rv = nghttp3_conn_bind_qpack_streams(httpconn_, qpack_enc_stream_id,
2071
+ qpack_dec_stream_id);
2072
+ rv != 0) {
2073
+ std::cerr << "nghttp3_conn_bind_qpack_streams: " << nghttp3_strerror(rv)
2074
+ << std::endl;
2075
+ return -1;
2076
+ }
2077
+
2078
+ if (!config.quiet) {
2079
+ fprintf(stderr,
2080
+ "http: QPACK streams encoder=%" PRIx64 " decoder=%" PRIx64 "\n",
2081
+ qpack_enc_stream_id, qpack_dec_stream_id);
2082
+ }
2083
+
2084
+ return 0;
2085
+ }
2086
+
2087
+ const std::vector<uint32_t> &Client::get_offered_versions() const {
2088
+ return offered_versions_;
2089
+ }
2090
+
2091
+ bool Client::get_early_data() const { return early_data_; };
2092
+
2093
+ namespace {
2094
+ int run(Client &c, const char *addr, const char *port,
2095
+ TLSClientContext &tls_ctx) {
2096
+ Address remote_addr, local_addr;
2097
+
2098
+ auto fd = create_sock(remote_addr, addr, port);
2099
+ if (fd == -1) {
2100
+ return -1;
2101
+ }
2102
+
2103
+ #ifdef HAVE_LINUX_RTNETLINK_H
2104
+ in_addr_union iau;
2105
+
2106
+ if (get_local_addr(iau, remote_addr) != 0) {
2107
+ std::cerr << "Could not get local address" << std::endl;
2108
+ close(fd);
2109
+ return -1;
2110
+ }
2111
+
2112
+ if (bind_addr(local_addr, fd, &iau, remote_addr.su.sa.sa_family) != 0) {
2113
+ close(fd);
2114
+ return -1;
2115
+ }
2116
+ #else // !HAVE_LINUX_RTNETLINK_H
2117
+ if (connect_sock(local_addr, fd, remote_addr) != 0) {
2118
+ close(fd);
2119
+ return -1;
2120
+ }
2121
+ #endif // !HAVE_LINUX_RTNETLINK_H
2122
+
2123
+ if (c.init(fd, local_addr, remote_addr, addr, port, tls_ctx) != 0) {
2124
+ return -1;
2125
+ }
2126
+
2127
+ // TODO Do we need this ?
2128
+ if (auto rv = c.on_write(); rv != 0) {
2129
+ return rv;
2130
+ }
2131
+
2132
+ ev_run(EV_DEFAULT, 0);
2133
+
2134
+ return 0;
2135
+ }
2136
+ } // namespace
2137
+
2138
+ namespace {
2139
+ std::string_view get_string(const char *uri, const http_parser_url &u,
2140
+ http_parser_url_fields f) {
2141
+ auto p = &u.field_data[f];
2142
+ return {uri + p->off, p->len};
2143
+ }
2144
+ } // namespace
2145
+
2146
+ namespace {
2147
+ int parse_uri(Request &req, const char *uri) {
2148
+ http_parser_url u;
2149
+
2150
+ http_parser_url_init(&u);
2151
+ if (http_parser_parse_url(uri, strlen(uri), /* is_connect = */ 0, &u) != 0) {
2152
+ return -1;
2153
+ }
2154
+
2155
+ if (!(u.field_set & (1 << UF_SCHEMA)) || !(u.field_set & (1 << UF_HOST))) {
2156
+ return -1;
2157
+ }
2158
+
2159
+ req.scheme = get_string(uri, u, UF_SCHEMA);
2160
+
2161
+ req.authority = get_string(uri, u, UF_HOST);
2162
+ if (util::numeric_host(req.authority.c_str(), AF_INET6)) {
2163
+ req.authority = '[' + req.authority + ']';
2164
+ }
2165
+ if (u.field_set & (1 << UF_PORT)) {
2166
+ req.authority += ':';
2167
+ req.authority += get_string(uri, u, UF_PORT);
2168
+ }
2169
+
2170
+ if (u.field_set & (1 << UF_PATH)) {
2171
+ req.path = get_string(uri, u, UF_PATH);
2172
+ } else {
2173
+ req.path = "/";
2174
+ }
2175
+
2176
+ if (u.field_set & (1 << UF_QUERY)) {
2177
+ req.path += '?';
2178
+ req.path += get_string(uri, u, UF_QUERY);
2179
+ }
2180
+
2181
+ return 0;
2182
+ }
2183
+ } // namespace
2184
+
2185
+ namespace {
2186
+ int parse_requests(char **argv, size_t argvlen) {
2187
+ for (size_t i = 0; i < argvlen; ++i) {
2188
+ auto uri = argv[i];
2189
+ Request req;
2190
+ if (parse_uri(req, uri) != 0) {
2191
+ std::cerr << "Could not parse URI: " << uri << std::endl;
2192
+ return -1;
2193
+ }
2194
+ config.requests.emplace_back(std::move(req));
2195
+ }
2196
+ return 0;
2197
+ }
2198
+ } // namespace
2199
+
2200
+ std::ofstream keylog_file;
2201
+
2202
+ namespace {
2203
+ void print_usage() {
2204
+ std::cerr << "Usage: client [OPTIONS] <HOST> <PORT> [<URI>...]" << std::endl;
2205
+ }
2206
+ } // namespace
2207
+
2208
+ namespace {
2209
+ void config_set_default(Config &config) {
2210
+ config = Config{};
2211
+ config.tx_loss_prob = 0.;
2212
+ config.rx_loss_prob = 0.;
2213
+ config.fd = -1;
2214
+ config.ciphers = util::crypto_default_ciphers();
2215
+ config.groups = util::crypto_default_groups();
2216
+ config.nstreams = 0;
2217
+ config.data = nullptr;
2218
+ config.datalen = 0;
2219
+ config.version = NGTCP2_PROTO_VER_V1;
2220
+ config.timeout = 30 * NGTCP2_SECONDS;
2221
+ config.http_method = "GET"sv;
2222
+ config.max_data = 15_m;
2223
+ config.max_stream_data_bidi_local = 6_m;
2224
+ config.max_stream_data_bidi_remote = 6_m;
2225
+ config.max_stream_data_uni = 6_m;
2226
+ config.max_window = 24_m;
2227
+ config.max_stream_window = 16_m;
2228
+ config.max_streams_uni = 100;
2229
+ config.cc_algo = NGTCP2_CC_ALGO_CUBIC;
2230
+ config.initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT;
2231
+ config.handshake_timeout = UINT64_MAX;
2232
+ config.ack_thresh = 2;
2233
+ }
2234
+ } // namespace
2235
+
2236
+ namespace {
2237
+ void print_help() {
2238
+ print_usage();
2239
+
2240
+ config_set_default(config);
2241
+
2242
+ std::cout << R"(
2243
+ <HOST> Remote server host (DNS name or IP address). In case of
2244
+ DNS name, it will be sent in TLS SNI extension.
2245
+ <PORT> Remote server port
2246
+ <URI> Remote URI
2247
+ Options:
2248
+ -t, --tx-loss=<P>
2249
+ The probability of losing outgoing packets. <P> must be
2250
+ [0.0, 1.0], inclusive. 0.0 means no packet loss. 1.0
2251
+ means 100% packet loss.
2252
+ -r, --rx-loss=<P>
2253
+ The probability of losing incoming packets. <P> must be
2254
+ [0.0, 1.0], inclusive. 0.0 means no packet loss. 1.0
2255
+ means 100% packet loss.
2256
+ -d, --data=<PATH>
2257
+ Read data from <PATH>, and send them as STREAM data.
2258
+ -n, --nstreams=<N>
2259
+ The number of requests. <URI>s are used in the order of
2260
+ appearance in the command-line. If the number of <URI>
2261
+ list is less than <N>, <URI> list is wrapped. It
2262
+ defaults to 0 which means the number of <URI> specified.
2263
+ -v, --version=<HEX>
2264
+ Specify QUIC version to use in hex string. If the given
2265
+ version is not supported by libngtcp2, client will use
2266
+ QUIC v1 long packet types. Instead of specifying hex
2267
+ string, there are special aliases available: "v1"
2268
+ indicates QUIC v1, and "v2" indicates QUIC v2.
2269
+ Default: )"
2270
+ << std::hex << "0x" << config.version << std::dec << R"(
2271
+ --preferred-versions=<HEX>[[,<HEX>]...]
2272
+ Specify QUIC versions in hex string in the order of
2273
+ preference. Client chooses one of those versions if
2274
+ client received Version Negotiation packet from server.
2275
+ These versions must be supported by libngtcp2. Instead
2276
+ of specifying hex string, there are special aliases
2277
+ available: "v1" indicates QUIC v1, and "v2" indicates
2278
+ QUIC v2.
2279
+ --available-versions=<HEX>[[,<HEX>]...]
2280
+ Specify QUIC versions in hex string that are sent in
2281
+ available_versions field of version_information
2282
+ transport parameter. This list can include a version
2283
+ which is not supported by libngtcp2. Instead of
2284
+ specifying hex string, there are special aliases
2285
+ available: "v1" indicates QUIC v1, and "v2" indicates
2286
+ QUIC v2.
2287
+ -q, --quiet Suppress debug output.
2288
+ -s, --show-secret
2289
+ Print out secrets unless --quiet is used.
2290
+ --timeout=<DURATION>
2291
+ Specify idle timeout.
2292
+ Default: )"
2293
+ << util::format_duration(config.timeout) << R"(
2294
+ --ciphers=<CIPHERS>
2295
+ Specify the cipher suite list to enable.
2296
+ Default: )"
2297
+ << config.ciphers << R"(
2298
+ --groups=<GROUPS>
2299
+ Specify the supported groups.
2300
+ Default: )"
2301
+ << config.groups << R"(
2302
+ --session-file=<PATH>
2303
+ Read/write TLS session from/to <PATH>. To resume a
2304
+ session, the previous session must be supplied with this
2305
+ option.
2306
+ --tp-file=<PATH>
2307
+ Read/write QUIC transport parameters from/to <PATH>. To
2308
+ send 0-RTT data, the transport parameters received from
2309
+ the previous session must be supplied with this option.
2310
+ --dcid=<DCID>
2311
+ Specify initial DCID. <DCID> is hex string. When
2312
+ decoded as binary, it should be at least 8 bytes and at
2313
+ most 18 bytes long.
2314
+ --scid=<SCID>
2315
+ Specify source connection ID. <SCID> is hex string. If
2316
+ an empty string is given, zero length connection ID is
2317
+ assumed.
2318
+ --change-local-addr=<DURATION>
2319
+ Client changes local address when <DURATION> elapse
2320
+ after handshake completes.
2321
+ --nat-rebinding
2322
+ When used with --change-local-addr, simulate NAT
2323
+ rebinding. In other words, client changes local
2324
+ address, but it does not start path validation.
2325
+ --key-update=<DURATION>
2326
+ Client initiates key update when <DURATION> elapse after
2327
+ handshake completes.
2328
+ -m, --http-method=<METHOD>
2329
+ Specify HTTP method. Default: )"
2330
+ << config.http_method << R"(
2331
+ --delay-stream=<DURATION>
2332
+ Delay sending STREAM data in 1-RTT for <DURATION> after
2333
+ handshake completes.
2334
+ --no-preferred-addr
2335
+ Do not try to use preferred address offered by server.
2336
+ --key=<PATH>
2337
+ The path to client private key PEM file.
2338
+ --cert=<PATH>
2339
+ The path to client certificate PEM file.
2340
+ --download=<PATH>
2341
+ The path to the directory to save a downloaded content.
2342
+ It is undefined if 2 concurrent requests write to the
2343
+ same file. If a request path does not contain a path
2344
+ component usable as a file name, it defaults to
2345
+ "index.html".
2346
+ --no-quic-dump
2347
+ Disables printing QUIC STREAM and CRYPTO frame data out.
2348
+ --no-http-dump
2349
+ Disables printing HTTP response body out.
2350
+ --qlog-file=<PATH>
2351
+ The path to write qlog. This option and --qlog-dir are
2352
+ mutually exclusive.
2353
+ --qlog-dir=<PATH>
2354
+ Path to the directory where qlog file is stored. The
2355
+ file name of each qlog is the Source Connection ID of
2356
+ client. This option and --qlog-file are mutually
2357
+ exclusive.
2358
+ --max-data=<SIZE>
2359
+ The initial connection-level flow control window.
2360
+ Default: )"
2361
+ << util::format_uint_iec(config.max_data) << R"(
2362
+ --max-stream-data-bidi-local=<SIZE>
2363
+ The initial stream-level flow control window for a
2364
+ bidirectional stream that the local endpoint initiates.
2365
+ Default: )"
2366
+ << util::format_uint_iec(config.max_stream_data_bidi_local) << R"(
2367
+ --max-stream-data-bidi-remote=<SIZE>
2368
+ The initial stream-level flow control window for a
2369
+ bidirectional stream that the remote endpoint initiates.
2370
+ Default: )"
2371
+ << util::format_uint_iec(config.max_stream_data_bidi_remote) << R"(
2372
+ --max-stream-data-uni=<SIZE>
2373
+ The initial stream-level flow control window for a
2374
+ unidirectional stream.
2375
+ Default: )"
2376
+ << util::format_uint_iec(config.max_stream_data_uni) << R"(
2377
+ --max-streams-bidi=<N>
2378
+ The number of the concurrent bidirectional streams.
2379
+ Default: )"
2380
+ << config.max_streams_bidi << R"(
2381
+ --max-streams-uni=<N>
2382
+ The number of the concurrent unidirectional streams.
2383
+ Default: )"
2384
+ << config.max_streams_uni << R"(
2385
+ --exit-on-first-stream-close
2386
+ Exit when a first client initialted HTTP stream is
2387
+ closed.
2388
+ --exit-on-all-streams-close
2389
+ Exit when all client initiated HTTP streams are closed.
2390
+ --disable-early-data
2391
+ Disable early data.
2392
+ --cc=(cubic|reno|bbr|bbr2)
2393
+ The name of congestion controller algorithm.
2394
+ Default: )"
2395
+ << util::strccalgo(config.cc_algo) << R"(
2396
+ --token-file=<PATH>
2397
+ Read/write token from/to <PATH>. Token is obtained from
2398
+ NEW_TOKEN frame from server.
2399
+ --sni=<DNSNAME>
2400
+ Send <DNSNAME> in TLS SNI, overriding the DNS name
2401
+ specified in <HOST>.
2402
+ --initial-rtt=<DURATION>
2403
+ Set an initial RTT.
2404
+ Default: )"
2405
+ << util::format_duration(config.initial_rtt) << R"(
2406
+ --max-window=<SIZE>
2407
+ Maximum connection-level flow control window size. The
2408
+ window auto-tuning is enabled if nonzero value is given,
2409
+ and window size is scaled up to this value.
2410
+ Default: )"
2411
+ << util::format_uint_iec(config.max_window) << R"(
2412
+ --max-stream-window=<SIZE>
2413
+ Maximum stream-level flow control window size. The
2414
+ window auto-tuning is enabled if nonzero value is given,
2415
+ and window size is scaled up to this value.
2416
+ Default: )"
2417
+ << util::format_uint_iec(config.max_stream_window) << R"(
2418
+ --max-udp-payload-size=<SIZE>
2419
+ Override maximum UDP payload size that client transmits.
2420
+ --handshake-timeout=<DURATION>
2421
+ Set the QUIC handshake timeout. It defaults to no
2422
+ timeout.
2423
+ --no-pmtud Disables Path MTU Discovery.
2424
+ --ack-thresh=<N>
2425
+ The minimum number of the received ACK eliciting packets
2426
+ that triggers immediate acknowledgement.
2427
+ Default: )"
2428
+ << config.ack_thresh << R"(
2429
+ -h, --help Display this help and exit.
2430
+
2431
+ ---
2432
+
2433
+ The <SIZE> argument is an integer and an optional unit (e.g., 10K is
2434
+ 10 * 1024). Units are K, M and G (powers of 1024).
2435
+
2436
+ The <DURATION> argument is an integer and an optional unit (e.g., 1s
2437
+ is 1 second and 500ms is 500 milliseconds). Units are h, m, s, ms,
2438
+ us, or ns (hours, minutes, seconds, milliseconds, microseconds, and
2439
+ nanoseconds respectively). If a unit is omitted, a second is used
2440
+ as unit.
2441
+
2442
+ The <HEX> argument is an hex string which must start with "0x"
2443
+ (e.g., 0x00000001).)"
2444
+ << std::endl;
2445
+ }
2446
+ } // namespace
2447
+
2448
+ int main(int argc, char **argv) {
2449
+ config_set_default(config);
2450
+ char *data_path = nullptr;
2451
+ const char *private_key_file = nullptr;
2452
+ const char *cert_file = nullptr;
2453
+
2454
+ for (;;) {
2455
+ static int flag = 0;
2456
+ constexpr static option long_opts[] = {
2457
+ {"help", no_argument, nullptr, 'h'},
2458
+ {"tx-loss", required_argument, nullptr, 't'},
2459
+ {"rx-loss", required_argument, nullptr, 'r'},
2460
+ {"data", required_argument, nullptr, 'd'},
2461
+ {"http-method", required_argument, nullptr, 'm'},
2462
+ {"nstreams", required_argument, nullptr, 'n'},
2463
+ {"version", required_argument, nullptr, 'v'},
2464
+ {"quiet", no_argument, nullptr, 'q'},
2465
+ {"show-secret", no_argument, nullptr, 's'},
2466
+ {"ciphers", required_argument, &flag, 1},
2467
+ {"groups", required_argument, &flag, 2},
2468
+ {"timeout", required_argument, &flag, 3},
2469
+ {"session-file", required_argument, &flag, 4},
2470
+ {"tp-file", required_argument, &flag, 5},
2471
+ {"dcid", required_argument, &flag, 6},
2472
+ {"change-local-addr", required_argument, &flag, 7},
2473
+ {"key-update", required_argument, &flag, 8},
2474
+ {"nat-rebinding", no_argument, &flag, 9},
2475
+ {"delay-stream", required_argument, &flag, 10},
2476
+ {"no-preferred-addr", no_argument, &flag, 11},
2477
+ {"key", required_argument, &flag, 12},
2478
+ {"cert", required_argument, &flag, 13},
2479
+ {"download", required_argument, &flag, 14},
2480
+ {"no-quic-dump", no_argument, &flag, 15},
2481
+ {"no-http-dump", no_argument, &flag, 16},
2482
+ {"qlog-file", required_argument, &flag, 17},
2483
+ {"max-data", required_argument, &flag, 18},
2484
+ {"max-stream-data-bidi-local", required_argument, &flag, 19},
2485
+ {"max-stream-data-bidi-remote", required_argument, &flag, 20},
2486
+ {"max-stream-data-uni", required_argument, &flag, 21},
2487
+ {"max-streams-bidi", required_argument, &flag, 22},
2488
+ {"max-streams-uni", required_argument, &flag, 23},
2489
+ {"exit-on-first-stream-close", no_argument, &flag, 24},
2490
+ {"disable-early-data", no_argument, &flag, 25},
2491
+ {"qlog-dir", required_argument, &flag, 26},
2492
+ {"cc", required_argument, &flag, 27},
2493
+ {"exit-on-all-streams-close", no_argument, &flag, 28},
2494
+ {"token-file", required_argument, &flag, 29},
2495
+ {"sni", required_argument, &flag, 30},
2496
+ {"initial-rtt", required_argument, &flag, 31},
2497
+ {"max-window", required_argument, &flag, 32},
2498
+ {"max-stream-window", required_argument, &flag, 33},
2499
+ {"scid", required_argument, &flag, 34},
2500
+ {"max-udp-payload-size", required_argument, &flag, 35},
2501
+ {"handshake-timeout", required_argument, &flag, 36},
2502
+ {"available-versions", required_argument, &flag, 37},
2503
+ {"no-pmtud", no_argument, &flag, 38},
2504
+ {"preferred-versions", required_argument, &flag, 39},
2505
+ {"ack-thresh", required_argument, &flag, 40},
2506
+ {nullptr, 0, nullptr, 0},
2507
+ };
2508
+
2509
+ auto optidx = 0;
2510
+ auto c = getopt_long(argc, argv, "d:him:n:qr:st:v:", long_opts, &optidx);
2511
+ if (c == -1) {
2512
+ break;
2513
+ }
2514
+ switch (c) {
2515
+ case 'd':
2516
+ // --data
2517
+ data_path = optarg;
2518
+ break;
2519
+ case 'h':
2520
+ // --help
2521
+ print_help();
2522
+ exit(EXIT_SUCCESS);
2523
+ case 'm':
2524
+ // --http-method
2525
+ config.http_method = optarg;
2526
+ break;
2527
+ case 'n':
2528
+ // --streams
2529
+ if (auto n = util::parse_uint(optarg); !n) {
2530
+ std::cerr << "streams: invalid argument" << std::endl;
2531
+ exit(EXIT_FAILURE);
2532
+ } else if (*n > NGTCP2_MAX_VARINT) {
2533
+ std::cerr << "streams: must not exceed " << NGTCP2_MAX_VARINT
2534
+ << std::endl;
2535
+ exit(EXIT_FAILURE);
2536
+ } else {
2537
+ config.nstreams = *n;
2538
+ }
2539
+ break;
2540
+ case 'q':
2541
+ // --quiet
2542
+ config.quiet = true;
2543
+ break;
2544
+ case 'r':
2545
+ // --rx-loss
2546
+ config.rx_loss_prob = strtod(optarg, nullptr);
2547
+ break;
2548
+ case 's':
2549
+ // --show-secret
2550
+ config.show_secret = true;
2551
+ break;
2552
+ case 't':
2553
+ // --tx-loss
2554
+ config.tx_loss_prob = strtod(optarg, nullptr);
2555
+ break;
2556
+ case 'v': {
2557
+ // --version
2558
+ if (optarg == "v1"sv) {
2559
+ config.version = NGTCP2_PROTO_VER_V1;
2560
+ break;
2561
+ }
2562
+ if (optarg == "v2"sv) {
2563
+ config.version = NGTCP2_PROTO_VER_V2;
2564
+ break;
2565
+ }
2566
+ auto rv = util::parse_version(optarg);
2567
+ if (!rv) {
2568
+ std::cerr << "version: invalid version " << std::quoted(optarg)
2569
+ << std::endl;
2570
+ exit(EXIT_FAILURE);
2571
+ }
2572
+ config.version = *rv;
2573
+ break;
2574
+ }
2575
+ case '?':
2576
+ print_usage();
2577
+ exit(EXIT_FAILURE);
2578
+ case 0:
2579
+ switch (flag) {
2580
+ case 1:
2581
+ // --ciphers
2582
+ config.ciphers = optarg;
2583
+ break;
2584
+ case 2:
2585
+ // --groups
2586
+ config.groups = optarg;
2587
+ break;
2588
+ case 3:
2589
+ // --timeout
2590
+ if (auto t = util::parse_duration(optarg); !t) {
2591
+ std::cerr << "timeout: invalid argument" << std::endl;
2592
+ exit(EXIT_FAILURE);
2593
+ } else {
2594
+ config.timeout = *t;
2595
+ }
2596
+ break;
2597
+ case 4:
2598
+ // --session-file
2599
+ config.session_file = optarg;
2600
+ break;
2601
+ case 5:
2602
+ // --tp-file
2603
+ config.tp_file = optarg;
2604
+ break;
2605
+ case 6: {
2606
+ // --dcid
2607
+ auto dcidlen2 = strlen(optarg);
2608
+ if (dcidlen2 % 2 || dcidlen2 / 2 < 8 || dcidlen2 / 2 > 18) {
2609
+ std::cerr << "dcid: wrong length" << std::endl;
2610
+ exit(EXIT_FAILURE);
2611
+ }
2612
+ auto dcid = util::decode_hex(optarg);
2613
+ ngtcp2_cid_init(&config.dcid,
2614
+ reinterpret_cast<const uint8_t *>(dcid.c_str()),
2615
+ dcid.size());
2616
+ break;
2617
+ }
2618
+ case 7:
2619
+ // --change-local-addr
2620
+ if (auto t = util::parse_duration(optarg); !t) {
2621
+ std::cerr << "change-local-addr: invalid argument" << std::endl;
2622
+ exit(EXIT_FAILURE);
2623
+ } else {
2624
+ config.change_local_addr = *t;
2625
+ }
2626
+ break;
2627
+ case 8:
2628
+ // --key-update
2629
+ if (auto t = util::parse_duration(optarg); !t) {
2630
+ std::cerr << "key-update: invalid argument" << std::endl;
2631
+ exit(EXIT_FAILURE);
2632
+ } else {
2633
+ config.key_update = *t;
2634
+ }
2635
+ break;
2636
+ case 9:
2637
+ // --nat-rebinding
2638
+ config.nat_rebinding = true;
2639
+ break;
2640
+ case 10:
2641
+ // --delay-stream
2642
+ if (auto t = util::parse_duration(optarg); !t) {
2643
+ std::cerr << "delay-stream: invalid argument" << std::endl;
2644
+ exit(EXIT_FAILURE);
2645
+ } else {
2646
+ config.delay_stream = *t;
2647
+ }
2648
+ break;
2649
+ case 11:
2650
+ // --no-preferred-addr
2651
+ config.no_preferred_addr = true;
2652
+ break;
2653
+ case 12:
2654
+ // --key
2655
+ private_key_file = optarg;
2656
+ break;
2657
+ case 13:
2658
+ // --cert
2659
+ cert_file = optarg;
2660
+ break;
2661
+ case 14:
2662
+ // --download
2663
+ config.download = optarg;
2664
+ break;
2665
+ case 15:
2666
+ // --no-quic-dump
2667
+ config.no_quic_dump = true;
2668
+ break;
2669
+ case 16:
2670
+ // --no-http-dump
2671
+ config.no_http_dump = true;
2672
+ break;
2673
+ case 17:
2674
+ // --qlog-file
2675
+ config.qlog_file = optarg;
2676
+ break;
2677
+ case 18:
2678
+ // --max-data
2679
+ if (auto n = util::parse_uint_iec(optarg); !n) {
2680
+ std::cerr << "max-data: invalid argument" << std::endl;
2681
+ exit(EXIT_FAILURE);
2682
+ } else {
2683
+ config.max_data = *n;
2684
+ }
2685
+ break;
2686
+ case 19:
2687
+ // --max-stream-data-bidi-local
2688
+ if (auto n = util::parse_uint_iec(optarg); !n) {
2689
+ std::cerr << "max-stream-data-bidi-local: invalid argument"
2690
+ << std::endl;
2691
+ exit(EXIT_FAILURE);
2692
+ } else {
2693
+ config.max_stream_data_bidi_local = *n;
2694
+ }
2695
+ break;
2696
+ case 20:
2697
+ // --max-stream-data-bidi-remote
2698
+ if (auto n = util::parse_uint_iec(optarg); !n) {
2699
+ std::cerr << "max-stream-data-bidi-remote: invalid argument"
2700
+ << std::endl;
2701
+ exit(EXIT_FAILURE);
2702
+ } else {
2703
+ config.max_stream_data_bidi_remote = *n;
2704
+ }
2705
+ break;
2706
+ case 21:
2707
+ // --max-stream-data-uni
2708
+ if (auto n = util::parse_uint_iec(optarg); !n) {
2709
+ std::cerr << "max-stream-data-uni: invalid argument" << std::endl;
2710
+ exit(EXIT_FAILURE);
2711
+ } else {
2712
+ config.max_stream_data_uni = *n;
2713
+ }
2714
+ break;
2715
+ case 22:
2716
+ // --max-streams-bidi
2717
+ if (auto n = util::parse_uint(optarg); !n) {
2718
+ std::cerr << "max-streams-bidi: invalid argument" << std::endl;
2719
+ exit(EXIT_FAILURE);
2720
+ } else {
2721
+ config.max_streams_bidi = *n;
2722
+ }
2723
+ break;
2724
+ case 23:
2725
+ // --max-streams-uni
2726
+ if (auto n = util::parse_uint(optarg); !n) {
2727
+ std::cerr << "max-streams-uni: invalid argument" << std::endl;
2728
+ exit(EXIT_FAILURE);
2729
+ } else {
2730
+ config.max_streams_uni = *n;
2731
+ }
2732
+ break;
2733
+ case 24:
2734
+ // --exit-on-first-stream-close
2735
+ config.exit_on_first_stream_close = true;
2736
+ break;
2737
+ case 25:
2738
+ // --disable-early-data
2739
+ config.disable_early_data = true;
2740
+ break;
2741
+ case 26:
2742
+ // --qlog-dir
2743
+ config.qlog_dir = optarg;
2744
+ break;
2745
+ case 27:
2746
+ // --cc
2747
+ if (strcmp("cubic", optarg) == 0) {
2748
+ config.cc_algo = NGTCP2_CC_ALGO_CUBIC;
2749
+ break;
2750
+ }
2751
+ if (strcmp("reno", optarg) == 0) {
2752
+ config.cc_algo = NGTCP2_CC_ALGO_RENO;
2753
+ break;
2754
+ }
2755
+ if (strcmp("bbr", optarg) == 0) {
2756
+ config.cc_algo = NGTCP2_CC_ALGO_BBR;
2757
+ break;
2758
+ }
2759
+ if (strcmp("bbr2", optarg) == 0) {
2760
+ config.cc_algo = NGTCP2_CC_ALGO_BBR2;
2761
+ break;
2762
+ }
2763
+ std::cerr << "cc: specify cubic, reno, bbr, or bbr2" << std::endl;
2764
+ exit(EXIT_FAILURE);
2765
+ case 28:
2766
+ // --exit-on-all-streams-close
2767
+ config.exit_on_all_streams_close = true;
2768
+ break;
2769
+ case 29:
2770
+ // --token-file
2771
+ config.token_file = optarg;
2772
+ break;
2773
+ case 30:
2774
+ // --sni
2775
+ config.sni = optarg;
2776
+ break;
2777
+ case 31:
2778
+ // --initial-rtt
2779
+ if (auto t = util::parse_duration(optarg); !t) {
2780
+ std::cerr << "initial-rtt: invalid argument" << std::endl;
2781
+ exit(EXIT_FAILURE);
2782
+ } else {
2783
+ config.initial_rtt = *t;
2784
+ }
2785
+ break;
2786
+ case 32:
2787
+ // --max-window
2788
+ if (auto n = util::parse_uint_iec(optarg); !n) {
2789
+ std::cerr << "max-window: invalid argument" << std::endl;
2790
+ exit(EXIT_FAILURE);
2791
+ } else {
2792
+ config.max_window = *n;
2793
+ }
2794
+ break;
2795
+ case 33:
2796
+ // --max-stream-window
2797
+ if (auto n = util::parse_uint_iec(optarg); !n) {
2798
+ std::cerr << "max-stream-window: invalid argument" << std::endl;
2799
+ exit(EXIT_FAILURE);
2800
+ } else {
2801
+ config.max_stream_window = *n;
2802
+ }
2803
+ break;
2804
+ case 34: {
2805
+ // --scid
2806
+ auto scid = util::decode_hex(optarg);
2807
+ ngtcp2_cid_init(&config.scid,
2808
+ reinterpret_cast<const uint8_t *>(scid.c_str()),
2809
+ scid.size());
2810
+ config.scid_present = true;
2811
+ break;
2812
+ }
2813
+ case 35:
2814
+ // --max-udp-payload-size
2815
+ if (auto n = util::parse_uint_iec(optarg); !n) {
2816
+ std::cerr << "max-udp-payload-size: invalid argument" << std::endl;
2817
+ exit(EXIT_FAILURE);
2818
+ } else if (*n > 64_k) {
2819
+ std::cerr << "max-udp-payload-size: must not exceed 65536"
2820
+ << std::endl;
2821
+ exit(EXIT_FAILURE);
2822
+ } else if (*n == 0) {
2823
+ std::cerr << "max-udp-payload-size: must not be 0" << std::endl;
2824
+ } else {
2825
+ config.max_udp_payload_size = *n;
2826
+ }
2827
+ break;
2828
+ case 36:
2829
+ // --handshake-timeout
2830
+ if (auto t = util::parse_duration(optarg); !t) {
2831
+ std::cerr << "handshake-timeout: invalid argument" << std::endl;
2832
+ exit(EXIT_FAILURE);
2833
+ } else {
2834
+ config.handshake_timeout = *t;
2835
+ }
2836
+ break;
2837
+ case 37: {
2838
+ // --available-versions
2839
+ if (strlen(optarg) == 0) {
2840
+ config.available_versions.resize(0);
2841
+ break;
2842
+ }
2843
+ auto l = util::split_str(optarg);
2844
+ config.available_versions.resize(l.size());
2845
+ auto it = std::begin(config.available_versions);
2846
+ for (const auto &k : l) {
2847
+ if (k == "v1"sv) {
2848
+ *it++ = NGTCP2_PROTO_VER_V1;
2849
+ continue;
2850
+ }
2851
+ if (k == "v2"sv) {
2852
+ *it++ = NGTCP2_PROTO_VER_V2;
2853
+ continue;
2854
+ }
2855
+ auto rv = util::parse_version(k);
2856
+ if (!rv) {
2857
+ std::cerr << "available-versions: invalid version "
2858
+ << std::quoted(k) << std::endl;
2859
+ exit(EXIT_FAILURE);
2860
+ }
2861
+ *it++ = *rv;
2862
+ }
2863
+ break;
2864
+ }
2865
+ case 38:
2866
+ // --no-pmtud
2867
+ config.no_pmtud = true;
2868
+ break;
2869
+ case 39: {
2870
+ // --preferred-versions
2871
+ auto l = util::split_str(optarg);
2872
+ if (l.size() > max_preferred_versionslen) {
2873
+ std::cerr << "preferred-versions: too many versions > "
2874
+ << max_preferred_versionslen << std::endl;
2875
+ }
2876
+ config.preferred_versions.resize(l.size());
2877
+ auto it = std::begin(config.preferred_versions);
2878
+ for (const auto &k : l) {
2879
+ if (k == "v1"sv) {
2880
+ *it++ = NGTCP2_PROTO_VER_V1;
2881
+ continue;
2882
+ }
2883
+ if (k == "v2"sv) {
2884
+ *it++ = NGTCP2_PROTO_VER_V2;
2885
+ continue;
2886
+ }
2887
+ auto rv = util::parse_version(k);
2888
+ if (!rv) {
2889
+ std::cerr << "preferred-versions: invalid version "
2890
+ << std::quoted(k) << std::endl;
2891
+ exit(EXIT_FAILURE);
2892
+ }
2893
+ if (!ngtcp2_is_supported_version(*rv)) {
2894
+ std::cerr << "preferred-versions: unsupported version "
2895
+ << std::quoted(k) << std::endl;
2896
+ exit(EXIT_FAILURE);
2897
+ }
2898
+ *it++ = *rv;
2899
+ }
2900
+ break;
2901
+ }
2902
+ case 40:
2903
+ // --ack-thresh
2904
+ if (auto n = util::parse_uint(optarg); !n) {
2905
+ std::cerr << "ack-thresh: invalid argument" << std::endl;
2906
+ exit(EXIT_FAILURE);
2907
+ } else if (*n > 100) {
2908
+ std::cerr << "ack-thresh: must not exceed 100" << std::endl;
2909
+ exit(EXIT_FAILURE);
2910
+ } else {
2911
+ config.ack_thresh = *n;
2912
+ }
2913
+ break;
2914
+ }
2915
+ break;
2916
+ default:
2917
+ break;
2918
+ };
2919
+ }
2920
+
2921
+ if (argc - optind < 2) {
2922
+ std::cerr << "Too few arguments" << std::endl;
2923
+ print_usage();
2924
+ exit(EXIT_FAILURE);
2925
+ }
2926
+
2927
+ if (!config.qlog_file.empty() && !config.qlog_dir.empty()) {
2928
+ std::cerr << "qlog-file and qlog-dir are mutually exclusive" << std::endl;
2929
+ exit(EXIT_FAILURE);
2930
+ }
2931
+
2932
+ if (config.exit_on_first_stream_close && config.exit_on_all_streams_close) {
2933
+ std::cerr << "exit-on-first-stream-close and exit-on-all-streams-close are "
2934
+ "mutually exclusive"
2935
+ << std::endl;
2936
+ exit(EXIT_FAILURE);
2937
+ }
2938
+
2939
+ if (data_path) {
2940
+ auto fd = open(data_path, O_RDONLY);
2941
+ if (fd == -1) {
2942
+ std::cerr << "data: Could not open file " << data_path << ": "
2943
+ << strerror(errno) << std::endl;
2944
+ exit(EXIT_FAILURE);
2945
+ }
2946
+ struct stat st;
2947
+ if (fstat(fd, &st) != 0) {
2948
+ std::cerr << "data: Could not stat file " << data_path << ": "
2949
+ << strerror(errno) << std::endl;
2950
+ exit(EXIT_FAILURE);
2951
+ }
2952
+ config.fd = fd;
2953
+ config.datalen = st.st_size;
2954
+ auto addr = mmap(nullptr, config.datalen, PROT_READ, MAP_SHARED, fd, 0);
2955
+ if (addr == MAP_FAILED) {
2956
+ std::cerr << "data: Could not mmap file " << data_path << ": "
2957
+ << strerror(errno) << std::endl;
2958
+ exit(EXIT_FAILURE);
2959
+ }
2960
+ config.data = static_cast<uint8_t *>(addr);
2961
+ }
2962
+
2963
+ auto addr = argv[optind++];
2964
+ auto port = argv[optind++];
2965
+
2966
+ if (parse_requests(&argv[optind], argc - optind) != 0) {
2967
+ exit(EXIT_FAILURE);
2968
+ }
2969
+
2970
+ if (!ngtcp2_is_reserved_version(config.version)) {
2971
+ if (!config.preferred_versions.empty() &&
2972
+ std::find(std::begin(config.preferred_versions),
2973
+ std::end(config.preferred_versions),
2974
+ config.version) == std::end(config.preferred_versions)) {
2975
+ std::cerr << "preferred-version: must include version " << std::hex
2976
+ << "0x" << config.version << std::dec << std::endl;
2977
+ exit(EXIT_FAILURE);
2978
+ }
2979
+
2980
+ if (!config.available_versions.empty() &&
2981
+ std::find(std::begin(config.available_versions),
2982
+ std::end(config.available_versions),
2983
+ config.version) == std::end(config.available_versions)) {
2984
+ std::cerr << "available-versions: must include version " << std::hex
2985
+ << "0x" << config.version << std::dec << std::endl;
2986
+ exit(EXIT_FAILURE);
2987
+ }
2988
+ }
2989
+
2990
+ if (config.nstreams == 0) {
2991
+ config.nstreams = config.requests.size();
2992
+ }
2993
+
2994
+ TLSClientContext tls_ctx;
2995
+ if (tls_ctx.init(private_key_file, cert_file) != 0) {
2996
+ exit(EXIT_FAILURE);
2997
+ }
2998
+
2999
+ auto ev_loop_d = defer(ev_loop_destroy, EV_DEFAULT);
3000
+
3001
+ auto keylog_filename = getenv("SSLKEYLOGFILE");
3002
+ if (keylog_filename) {
3003
+ keylog_file.open(keylog_filename, std::ios_base::app);
3004
+ if (keylog_file) {
3005
+ tls_ctx.enable_keylog();
3006
+ }
3007
+ }
3008
+
3009
+ if (util::generate_secret(config.static_secret.data(),
3010
+ config.static_secret.size()) != 0) {
3011
+ std::cerr << "Unable to generate static secret" << std::endl;
3012
+ exit(EXIT_FAILURE);
3013
+ }
3014
+
3015
+ auto client_chosen_version = config.version;
3016
+
3017
+ for (;;) {
3018
+ Client c(EV_DEFAULT, client_chosen_version, config.version);
3019
+
3020
+ if (run(c, addr, port, tls_ctx) != 0) {
3021
+ exit(EXIT_FAILURE);
3022
+ }
3023
+
3024
+ if (config.preferred_versions.empty()) {
3025
+ break;
3026
+ }
3027
+
3028
+ auto &offered_versions = c.get_offered_versions();
3029
+ if (offered_versions.empty()) {
3030
+ break;
3031
+ }
3032
+
3033
+ client_chosen_version = ngtcp2_select_version(
3034
+ config.preferred_versions.data(), config.preferred_versions.size(),
3035
+ offered_versions.data(), offered_versions.size());
3036
+
3037
+ if (client_chosen_version == 0) {
3038
+ std::cerr << "Unable to select a version" << std::endl;
3039
+ exit(EXIT_FAILURE);
3040
+ }
3041
+
3042
+ if (!config.quiet) {
3043
+ std::cerr << "Client selected version " << std::hex << "0x"
3044
+ << client_chosen_version << std::dec << std::endl;
3045
+ }
3046
+ }
3047
+
3048
+ return EXIT_SUCCESS;
3049
+ }