protocol-quic 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }