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