quic 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/quic/extconf.rb CHANGED
@@ -2,53 +2,163 @@
2
2
 
3
3
  require "mkmf"
4
4
  require "mini_portile2"
5
+ require "shellwords"
6
+ require "fileutils"
5
7
 
6
- LIBRESSL_VERSION = "4.3.1"
7
- LIBRESSL_SHA256 = "c2db42ace14e7d5419826fab35a742ec6e4d12725a051a51d0cea3c10ba0fa50"
8
- NGTCP2_VERSION = "1.22.1"
9
- NGTCP2_SHA256 = "063d80531acac0ddbbc1b9d12829a824edc2abe8dba2e632fd1ce15cfd5632f9"
8
+ NGTCP2_VERSION = "1.25.0"
9
+ NGTCP2_SHA256 = "1c0843076528a87b65e9a9d455100941f4cb65d44f96c5da6ae56df146043955"
10
+ # The picotls revision that ngtcp2 1.25.0 is tested against (see ngtcp2's README).
11
+ PICOTLS_COMMIT = "f07f1c8c68b237f1468bc1f1fe1b68aba3ff23b4"
12
+ PICOTLS_SHA256 = "6ecb4f8d9946c2b9342300b29627dc34e746a551d438d35af325a4495d62a6e3"
13
+ # Bump when ext/quic/patches/picotls/ changes; installed? only checks for the archive.
14
+ PICOTLS_PATCH_LEVEL = "p1"
10
15
 
11
- libressl = MiniPortile.new("libressl", LIBRESSL_VERSION)
12
- libressl.files = [{
13
- url: "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-#{LIBRESSL_VERSION}.tar.gz",
14
- sha256: LIBRESSL_SHA256
16
+ # Locate the host's libcrypto. ngtcp2 and picotls are vendored and statically
17
+ # linked, but the crypto primitives and X.509 come from whatever OpenSSL (or
18
+ # LibreSSL) the system provides, linked dynamically. Sharing one libcrypto with
19
+ # Ruby's own openssl extension is the point: two copies in one process
20
+ # interpose on each other's global symbols.
21
+ #
22
+ # Everything downstream resolves OpenSSL through pkg-config -- this file,
23
+ # ngtcp2's configure, and the `openssl` entry in libngtcp2_crypto_picotls.pc's
24
+ # Requires.private -- so --with-openssl-dir is honoured by putting its prefix
25
+ # on PKG_CONFIG_PATH once, here. That keeps all three agreeing on one install
26
+ # instead of each making its own choice.
27
+ _openssl_incdir, openssl_libdir = dir_config("openssl")
28
+ if openssl_libdir
29
+ ENV["PKG_CONFIG_PATH"] =
30
+ [File.join(openssl_libdir, "pkgconfig"), ENV["PKG_CONFIG_PATH"]].compact.join(File::PATH_SEPARATOR)
31
+ end
32
+
33
+ unless pkg_config("openssl")
34
+ abort "OpenSSL not found. Install its development package (libssl-dev, " \
35
+ "openssl-devel, ...), or point at a prefix whose lib/pkgconfig holds " \
36
+ "openssl.pc with --with-openssl-dir=/path."
37
+ end
38
+ # picotls is compiled by hand below and needs the include path on its own.
39
+ openssl_cflags = pkg_config("openssl", "cflags-only-I").to_s.strip
40
+
41
+ # picotls has no release tarballs, and its CMake build needs the picotest
42
+ # submodule, which GitHub archive tarballs do not contain. Build only the two
43
+ # libraries we need (core + OpenSSL-API backend) into a single libpicotls.a.
44
+ class PicotlsRecipe < MiniPortile
45
+ SOURCES = %w[lib/picotls.c lib/hpke.c lib/pembase64.c lib/openssl.c].freeze
46
+
47
+ attr_accessor :openssl_cflags
48
+
49
+ def configure
50
+ end
51
+
52
+ def configured?
53
+ true
54
+ end
55
+
56
+ def compile
57
+ cflags = %w[-std=gnu99 -O2 -fPIC -fvisibility=hidden -Iinclude] + openssl_cflags.to_s.shellsplit
58
+ objects = SOURCES.map do |src|
59
+ obj = File.basename(src, ".c") + ".o"
60
+ execute("compile", [*cc_cmd.shellsplit, *cflags, "-c", src, "-o", obj])
61
+ obj
62
+ end
63
+ ar = (ENV["AR"] || RbConfig::CONFIG["AR"] || "ar").shellsplit
64
+ execute("compile", [*ar, "rcs", "libpicotls.a", *objects])
65
+ end
66
+
67
+ def install
68
+ FileUtils.mkdir_p([lib_path, include_path])
69
+ FileUtils.cp(File.join(work_path, "libpicotls.a"), lib_path)
70
+ FileUtils.cp_r(Dir.glob(File.join(work_path, "include", "*")), include_path)
71
+ end
72
+
73
+ def installed?
74
+ File.exist?(File.join(lib_path, "libpicotls.a"))
75
+ end
76
+ end
77
+
78
+ picotls = PicotlsRecipe.new("picotls", "#{PICOTLS_COMMIT[0, 7]}-#{PICOTLS_PATCH_LEVEL}")
79
+ picotls.files = [{
80
+ url: "https://github.com/h2o/picotls/archive/#{PICOTLS_COMMIT}.tar.gz",
81
+ sha256: PICOTLS_SHA256
15
82
  }]
16
- libressl.configure_options = %w[--disable-shared --with-pic]
17
- libressl.cook
18
- libressl.activate
83
+ picotls.patch_files = Dir.glob(File.join(__dir__, "patches", "picotls", "*.patch")).sort
84
+ picotls.openssl_cflags = openssl_cflags
85
+ picotls.cook
86
+ # Deliberately no picotls.activate: mkmf_config passes -I/-L below, and not
87
+ # activating keeps the extra directory out of CPATH.
19
88
 
20
89
  ngtcp2 = MiniPortile.new("ngtcp2", NGTCP2_VERSION)
21
90
  ngtcp2.files = [{
22
91
  url: "https://github.com/ngtcp2/ngtcp2/releases/download/v#{NGTCP2_VERSION}/ngtcp2-#{NGTCP2_VERSION}.tar.gz",
23
92
  sha256: NGTCP2_SHA256
24
93
  }]
25
- ngtcp2.configure_options = %W[
26
- --enable-lib-only
27
- --disable-shared
28
- --with-pic
29
- PKG_CONFIG_PATH=#{libressl.path}/lib/pkgconfig
94
+ ngtcp2.configure_options = [
95
+ "--enable-lib-only",
96
+ "--disable-shared",
97
+ "--with-pic",
98
+ # Skip the quictls/libressl/ossl helpers; only libngtcp2_crypto_picotls.
99
+ "--without-openssl",
100
+ "--with-picotls",
101
+ # crypto/picotls declares lib_LIBRARIES, not lib_LTLIBRARIES, so it is built
102
+ # by plain ar rather than libtool and --with-pic does not reach it. Without
103
+ # -fPIC the archive cannot be linked into quic.so.
104
+ "CFLAGS=-g -O2 -fPIC",
105
+ "PICOTLS_CFLAGS=-I#{picotls.include_path}",
106
+ "PICOTLS_LIBS=-L#{picotls.lib_path} -lpicotls"
30
107
  ]
108
+ # ngtcp2 still probes for OpenSSL under --without-openssl: its picotls check
109
+ # links the conftest against libcrypto. It finds it through the same
110
+ # PKG_CONFIG_PATH set above, which the configure subprocess inherits.
31
111
  ngtcp2.cook
32
112
  ngtcp2.activate
33
113
 
34
- # Wire static archives into mkmf in reverse dependency order. mkmf_config
35
- # prepends to $libs, so the leftmost entry on the final link line ends up being
36
- # the last one we add here (ngtcp2_crypto_libressl), satisfying static-link
37
- # ordering: ngtcp2_crypto_libressl -> ngtcp2 -> ssl -> crypto.
38
- libressl.mkmf_config(pkg: "libcrypto", static: "crypto")
39
- libressl.mkmf_config(pkg: "libssl", static: "ssl")
40
- ngtcp2.mkmf_config(pkg: "libngtcp2", static: "ngtcp2")
41
- ngtcp2.mkmf_config(pkg: "libngtcp2_crypto_libressl", static: "ngtcp2_crypto_libressl")
114
+ # Wire the vendored static archives into mkmf in reverse dependency order.
115
+ # mkmf_config prepends to $libs, so the leftmost entry on the final link line
116
+ # ends up being the last one added here, satisfying static-link ordering:
117
+ # ngtcp2_crypto_picotls -> ngtcp2 -> picotls -> the -lssl -lcrypto that
118
+ # pkg_config("openssl") already put there.
119
+ #
120
+ # Deliberately not the `pkg:` form. Resolving libngtcp2_crypto_picotls.pc
121
+ # would follow its `Requires.private: libngtcp2, openssl` with
122
+ # `pkg-config --static`, which drags in the host libcrypto's own static
123
+ # dependency list (on Ubuntu: -l:libjitterentropy.a -lz -lzstd). Those belong
124
+ # to a static libcrypto we are not linking, and the archives they name are
125
+ # often not even installed. The recipe's own include/lib paths are all we
126
+ # need.
127
+ picotls.mkmf_config(static: "picotls")
128
+ ngtcp2.mkmf_config(static: "ngtcp2")
129
+ ngtcp2.mkmf_config(static: "ngtcp2_crypto_picotls")
42
130
 
131
+ # picotls's OpenSSL backend, and the X25519 patch in ext/quic/patches/picotls/,
132
+ # both need the raw public key API introduced in OpenSSL 1.1.1. LibreSSL
133
+ # reports 0x20000000L here, so one check covers both.
134
+ abort "OpenSSL 1.1.1 or later is required" unless try_compile(<<~C)
135
+ #include <openssl/opensslv.h>
136
+ #if OPENSSL_VERSION_NUMBER < 0x10101000L
137
+ #error OpenSSL is too old
138
+ #endif
139
+ int main(void) { return 0; }
140
+ C
43
141
  abort "ngtcp2 header missing" unless have_header("ngtcp2/ngtcp2.h")
44
- abort "ngtcp2_crypto_quictls header missing" unless have_header("ngtcp2/ngtcp2_crypto_quictls.h")
45
- abort "openssl/ssl.h missing" unless have_header("openssl/ssl.h")
142
+ abort "picotls header missing" unless have_header("picotls.h")
143
+ abort "picotls/openssl.h missing" unless have_header("picotls/openssl.h")
144
+ abort "ngtcp2_crypto_picotls header missing" unless have_header("ngtcp2/ngtcp2_crypto_picotls.h")
145
+ abort "picotls was built without X25519 (patch not applied?)" unless try_compile(<<~C)
146
+ #include <picotls/openssl.h>
147
+ #if !PTLS_OPENSSL_HAVE_X25519
148
+ #error X25519 unavailable
149
+ #endif
150
+ C
46
151
  # ngtcp2_conn_client_new and most other ngtcp2 entry points are exposed as
47
152
  # ABI-versioned macros, so use non-macro symbols for link verification.
48
153
  abort "libngtcp2 not linkable" unless have_func("ngtcp2_version", "ngtcp2/ngtcp2.h")
49
- abort "libngtcp2_crypto_libressl not linkable" unless have_func("ngtcp2_crypto_quictls_init", "ngtcp2/ngtcp2_crypto_quictls.h")
50
- abort "libssl not linkable" unless have_func("OpenSSL_version", "openssl/opensslv.h")
154
+ abort "libngtcp2_crypto_picotls not linkable" unless
155
+ have_func("ngtcp2_crypto_picotls_configure_client_context", %w[picotls.h ngtcp2/ngtcp2_crypto_picotls.h])
156
+ abort "libcrypto not linkable" unless have_func("OpenSSL_version", "openssl/crypto.h")
51
157
 
158
+ # picotls has neither a version macro nor releases, so QUIC.library_versions
159
+ # reports the commit the extension was built against.
160
+ append_cflags("-DQUIC_PICOTLS_COMMIT=\\\"#{PICOTLS_COMMIT}\\\"")
52
161
  append_cflags("-fvisibility=hidden")
162
+ append_ldflags("-Wl,--exclude-libs,ALL") # Unexport symbols from statically linked libraries
53
163
 
54
164
  create_makefile("quic/quic")
@@ -0,0 +1,74 @@
1
+ Subject: [PATCH] openssl: use the raw public key API for X25519
2
+
3
+ LibreSSL does not provide EVP_PKEY_get1_tls_encodedpoint() /
4
+ EVP_PKEY_set1_tls_encodedpoint(), so picotls disables ptls_openssl_x25519
5
+ when built against LibreSSL. Switch the EVP-based X25519 key exchange to
6
+ EVP_PKEY_get_raw_public_key() / EVP_PKEY_new_raw_public_key() and drop the
7
+ LibreSSL exclusion.
8
+
9
+ Verified with LibreSSL 4.3.1 and OpenSSL 3.5.7 (picotls test-openssl.t,
10
+ including x25519-to-minicrypto / x25519-from-minicrypto).
11
+
12
+ Not upstream. Requires OpenSSL >= 1.1.1 (1.1.0 lacks the raw key API).
13
+ Applies to h2o/picotls@b84869f41414b6d0148db7728f1cf12f5b544874.
14
+
15
+ diff --git a/include/picotls/openssl.h b/include/picotls/openssl.h
16
+ index 37a0740..04e4653 100644
17
+ --- a/include/picotls/openssl.h
18
+ +++ b/include/picotls/openssl.h
19
+ @@ -60,7 +60,7 @@ extern ptls_key_exchange_algorithm_t ptls_openssl_secp521r1;
20
+ #ifdef EVP_PKEY_ED25519
21
+ #define PTLS_OPENSSL_HAVE_ED25519 1
22
+ #endif
23
+ -#if defined(NID_X25519) && !defined(LIBRESSL_VERSION_NUMBER)
24
+ +#if defined(NID_X25519)
25
+ #define PTLS_OPENSSL_HAVE_X25519 1
26
+ #define PTLS_OPENSSL_HAS_X25519 1 /* deprecated; use HAVE_ */
27
+ extern ptls_key_exchange_algorithm_t ptls_openssl_x25519;
28
+ diff --git a/lib/openssl.c b/lib/openssl.c
29
+ index d6d073b..1c7e492 100644
30
+ --- a/lib/openssl.c
31
+ +++ b/lib/openssl.c
32
+ @@ -559,15 +559,7 @@ static int evp_keyex_on_exchange(ptls_key_exchange_context_t **_ctx, int release
33
+ #undef X25519_KEY_SIZE
34
+ #endif
35
+
36
+ - if ((evppeer = EVP_PKEY_new()) == NULL) {
37
+ - ret = PTLS_ERROR_NO_MEMORY;
38
+ - goto Exit;
39
+ - }
40
+ - if (EVP_PKEY_copy_parameters(evppeer, ctx->privkey) <= 0) {
41
+ - ret = PTLS_ERROR_LIBRARY;
42
+ - goto Exit;
43
+ - }
44
+ - if (EVP_PKEY_set1_tls_encodedpoint(evppeer, peerkey.base, peerkey.len) <= 0) {
45
+ + if ((evppeer = EVP_PKEY_new_raw_public_key(EVP_PKEY_id(ctx->privkey), NULL, peerkey.base, peerkey.len)) == NULL) {
46
+ ret = PTLS_ERROR_LIBRARY;
47
+ goto Exit;
48
+ }
49
+ @@ -629,10 +621,21 @@ static int evp_keyex_init(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange
50
+ *ctx = (struct st_evp_keyex_context_t){{algo, {NULL}, evp_keyex_on_exchange}, pkey};
51
+
52
+ /* set public key */
53
+ - if ((ctx->super.pubkey.len = EVP_PKEY_get1_tls_encodedpoint(ctx->privkey, &ctx->super.pubkey.base)) == 0) {
54
+ - ctx->super.pubkey.base = NULL;
55
+ - ret = PTLS_ERROR_NO_MEMORY;
56
+ - goto Exit;
57
+ + {
58
+ + size_t publen = 0;
59
+ + if (EVP_PKEY_get_raw_public_key(ctx->privkey, NULL, &publen) != 1 || publen == 0) {
60
+ + ret = PTLS_ERROR_LIBRARY;
61
+ + goto Exit;
62
+ + }
63
+ + if ((ctx->super.pubkey.base = OPENSSL_malloc(publen)) == NULL) {
64
+ + ret = PTLS_ERROR_NO_MEMORY;
65
+ + goto Exit;
66
+ + }
67
+ + if (EVP_PKEY_get_raw_public_key(ctx->privkey, ctx->super.pubkey.base, &publen) != 1) {
68
+ + ret = PTLS_ERROR_LIBRARY;
69
+ + goto Exit;
70
+ + }
71
+ + ctx->super.pubkey.len = publen;
72
+ }
73
+
74
+ *_ctx = &ctx->super;
data/ext/quic/quic.c CHANGED
@@ -1,24 +1,25 @@
1
1
  #include "quic.h"
2
2
  #include "stream.h"
3
3
 
4
- VALUE rb_mQuic;
5
- VALUE rb_mQuicConnection;
6
- VALUE rb_cQuicConnectionClient;
4
+ VALUE rb_mQUIC;
5
+ VALUE rb_mQUICConnection;
6
+ VALUE rb_cQUICConnectionClient;
7
7
 
8
- VALUE rb_eQuicError;
9
- VALUE rb_eQuicErrorProto;
10
- VALUE rb_eQuicErrorDropConn;
11
- VALUE rb_eQuicErrorRetry;
12
- VALUE rb_eQuicErrorClosed;
13
- VALUE rb_eQuicErrorCryptoError;
14
- VALUE rb_eQuicErrorHandshakeTimeout;
15
- VALUE rb_eQuicErrorFlowControl;
16
- VALUE rb_eQuicErrorUnknown;
17
- VALUE rb_eQuicErrorWaitReadable;
18
- VALUE rb_eQuicErrorWaitWritable;
19
- VALUE rb_eQuicErrorStreamClosed;
20
- VALUE rb_eQuicErrorStreamReset;
21
- VALUE rb_eQuicErrorNotBound;
8
+ VALUE rb_eQUICError;
9
+ VALUE rb_eQUICErrorProto;
10
+ VALUE rb_eQUICErrorDropConn;
11
+ VALUE rb_eQUICErrorRetry;
12
+ VALUE rb_eQUICErrorClosed;
13
+ VALUE rb_eQUICErrorCryptoError;
14
+ VALUE rb_eQUICErrorCertificateVerifyFailed;
15
+ VALUE rb_eQUICErrorHandshakeTimeout;
16
+ VALUE rb_eQUICErrorFlowControl;
17
+ VALUE rb_eQUICErrorUnknown;
18
+ VALUE rb_eQUICErrorWaitReadable;
19
+ VALUE rb_eQUICErrorWaitWritable;
20
+ VALUE rb_eQUICErrorStreamClosed;
21
+ VALUE rb_eQUICErrorStreamReset;
22
+ VALUE rb_eQUICErrorNotBound;
22
23
 
23
24
  static VALUE
24
25
  quic_library_versions(VALUE self)
@@ -29,6 +30,10 @@ quic_library_versions(VALUE self)
29
30
  rb_str_new_cstr(ngtcp2_version(0)->version_str));
30
31
  rb_hash_aset(h, ID2SYM(rb_intern("openssl")),
31
32
  rb_str_new_cstr(OpenSSL_version(OPENSSL_VERSION)));
33
+ /* picotls has neither a version macro nor releases, so report the commit
34
+ extconf.rb built against (passed in as -DQUIC_PICOTLS_COMMIT). */
35
+ rb_hash_aset(h, ID2SYM(rb_intern("picotls")),
36
+ rb_str_new_cstr(QUIC_PICOTLS_COMMIT));
32
37
  return h;
33
38
  }
34
39
 
@@ -38,35 +43,35 @@ quic_raise_ngtcp2_error(int rv)
38
43
  VALUE cls;
39
44
  switch (rv) {
40
45
  case NGTCP2_ERR_PROTO:
41
- cls = rb_eQuicErrorProto;
46
+ cls = rb_eQUICErrorProto;
42
47
  break;
43
48
  case NGTCP2_ERR_DROP_CONN:
44
- cls = rb_eQuicErrorDropConn;
49
+ cls = rb_eQUICErrorDropConn;
45
50
  break;
46
51
  case NGTCP2_ERR_RETRY:
47
- cls = rb_eQuicErrorRetry;
52
+ cls = rb_eQUICErrorRetry;
48
53
  break;
49
54
  case NGTCP2_ERR_CLOSING:
50
55
  case NGTCP2_ERR_DRAINING:
51
- cls = rb_eQuicErrorClosed;
56
+ cls = rb_eQUICErrorClosed;
52
57
  break;
53
58
  case NGTCP2_ERR_IDLE_CLOSE:
54
- cls = rb_eQuicErrorClosed;
59
+ cls = rb_eQUICErrorClosed;
55
60
  break;
56
61
  case NGTCP2_ERR_CRYPTO:
57
- cls = rb_eQuicErrorCryptoError;
62
+ cls = rb_eQUICErrorCryptoError;
58
63
  break;
59
64
  case NGTCP2_ERR_HANDSHAKE_TIMEOUT:
60
- cls = rb_eQuicErrorHandshakeTimeout;
65
+ cls = rb_eQUICErrorHandshakeTimeout;
61
66
  break;
62
67
  case NGTCP2_ERR_STREAM_DATA_BLOCKED:
63
- cls = rb_eQuicErrorFlowControl;
68
+ cls = rb_eQUICErrorFlowControl;
64
69
  break;
65
70
  case NGTCP2_ERR_STREAM_SHUT_WR:
66
- cls = rb_eQuicErrorStreamClosed;
71
+ cls = rb_eQUICErrorStreamClosed;
67
72
  break;
68
73
  default:
69
- cls = rb_eQuicErrorUnknown;
74
+ cls = rb_eQUICErrorUnknown;
70
75
  break;
71
76
  }
72
77
 
@@ -78,37 +83,44 @@ quic_raise_ngtcp2_error(int rv)
78
83
  RUBY_FUNC_EXPORTED void
79
84
  Init_quic(void)
80
85
  {
81
- rb_mQuic = rb_define_module("Quic");
82
- rb_mQuicConnection = rb_define_module_under(rb_mQuic, "Connection");
83
- rb_define_singleton_method(rb_mQuic, "library_versions", quic_library_versions, 0);
86
+ rb_mQUIC = rb_define_module("QUIC");
87
+ rb_mQUICConnection = rb_define_module_under(rb_mQUIC, "Connection");
88
+ rb_define_singleton_method(rb_mQUIC, "library_versions", quic_library_versions, 0);
84
89
 
85
- /* Quic::Error is defined in Ruby (lib/quic.rb) before this Init_quic runs,
90
+ /* QUIC::Error is defined in Ruby (lib/quic.rb) before this Init_quic runs,
86
91
  so rb_const_get retrieves the already-defined base class. Subclasses live
87
- under Quic::Error::<Name>. */
88
- rb_eQuicError = rb_const_get(rb_mQuic, rb_intern("Error"));
89
- rb_eQuicErrorProto = rb_define_class_under(rb_eQuicError, "Proto", rb_eQuicError);
90
- rb_eQuicErrorDropConn = rb_define_class_under(rb_eQuicError, "DropConn", rb_eQuicError);
91
- rb_eQuicErrorRetry = rb_define_class_under(rb_eQuicError, "Retry", rb_eQuicError);
92
- rb_eQuicErrorClosed = rb_define_class_under(rb_eQuicError, "Closed", rb_eQuicError);
93
- rb_eQuicErrorCryptoError = rb_define_class_under(rb_eQuicError, "CryptoError", rb_eQuicError);
94
- rb_eQuicErrorHandshakeTimeout = rb_define_class_under(rb_eQuicError, "HandshakeTimeout", rb_eQuicError);
95
- rb_eQuicErrorFlowControl = rb_define_class_under(rb_eQuicError, "FlowControl", rb_eQuicError);
96
- rb_eQuicErrorUnknown = rb_define_class_under(rb_eQuicError, "Unknown", rb_eQuicError);
92
+ under QUIC::Error::<Name>. */
93
+ rb_eQUICError = rb_const_get(rb_mQUIC, rb_intern("Error"));
94
+ rb_eQUICErrorProto = rb_define_class_under(rb_eQUICError, "Proto", rb_eQUICError);
95
+ rb_eQUICErrorDropConn = rb_define_class_under(rb_eQUICError, "DropConn", rb_eQUICError);
96
+ rb_eQUICErrorRetry = rb_define_class_under(rb_eQUICError, "Retry", rb_eQUICError);
97
+ rb_eQUICErrorClosed = rb_define_class_under(rb_eQUICError, "Closed", rb_eQUICError);
98
+ rb_eQUICErrorCryptoError = rb_define_class_under(rb_eQUICError, "CryptoError", rb_eQUICError);
99
+ /* The TLS alert this client sent, when read_pkt fails in the handshake.
100
+ Alerts received from the peer are not recorded by ngtcp2. */
101
+ rb_define_attr(rb_eQUICErrorCryptoError, "tls_alert", 1, 0);
102
+ rb_eQUICErrorCertificateVerifyFailed =
103
+ rb_define_class_under(rb_eQUICError, "CertificateVerifyFailed", rb_eQUICErrorCryptoError);
104
+ /* X509_V_ERR_* from the chain / name check, or nil when no certificate came. */
105
+ rb_define_attr(rb_eQUICErrorCertificateVerifyFailed, "verify_result", 1, 0);
106
+ rb_eQUICErrorHandshakeTimeout = rb_define_class_under(rb_eQUICError, "HandshakeTimeout", rb_eQUICError);
107
+ rb_eQUICErrorFlowControl = rb_define_class_under(rb_eQUICError, "FlowControl", rb_eQUICError);
108
+ rb_eQUICErrorUnknown = rb_define_class_under(rb_eQUICError, "Unknown", rb_eQUICError);
97
109
 
98
110
  /* IO-style non-blocking errors. Mix in the standard IO::Wait{Readable,Writable}
99
- modules so callers can `rescue IO::WaitReadable` (or the Quic subclass)
111
+ modules so callers can `rescue IO::WaitReadable` (or the QUIC subclass)
100
112
  interchangeably with stdlib IO objects. */
101
113
  VALUE io_wait_readable = rb_const_get(rb_cIO, rb_intern("WaitReadable"));
102
114
  VALUE io_wait_writable = rb_const_get(rb_cIO, rb_intern("WaitWritable"));
103
- rb_eQuicErrorWaitReadable = rb_define_class_under(rb_eQuicError, "WaitReadable", rb_eQuicError);
104
- rb_include_module(rb_eQuicErrorWaitReadable, io_wait_readable);
105
- rb_eQuicErrorWaitWritable = rb_define_class_under(rb_eQuicError, "WaitWritable", rb_eQuicError);
106
- rb_include_module(rb_eQuicErrorWaitWritable, io_wait_writable);
115
+ rb_eQUICErrorWaitReadable = rb_define_class_under(rb_eQUICError, "WaitReadable", rb_eQUICError);
116
+ rb_include_module(rb_eQUICErrorWaitReadable, io_wait_readable);
117
+ rb_eQUICErrorWaitWritable = rb_define_class_under(rb_eQUICError, "WaitWritable", rb_eQUICError);
118
+ rb_include_module(rb_eQUICErrorWaitWritable, io_wait_writable);
107
119
 
108
- rb_eQuicErrorStreamClosed = rb_define_class_under(rb_eQuicError, "StreamClosed", rb_eQuicError);
109
- rb_eQuicErrorStreamReset = rb_define_class_under(rb_eQuicError, "StreamReset", rb_eQuicError);
110
- rb_eQuicErrorNotBound = rb_define_class_under(rb_eQuicError, "NotBound", rb_eQuicError);
120
+ rb_eQUICErrorStreamClosed = rb_define_class_under(rb_eQUICError, "StreamClosed", rb_eQUICError);
121
+ rb_eQUICErrorStreamReset = rb_define_class_under(rb_eQUICError, "StreamReset", rb_eQUICError);
122
+ rb_eQUICErrorNotBound = rb_define_class_under(rb_eQUICError, "NotBound", rb_eQUICError);
111
123
 
112
- Init_quic_connection_client(rb_mQuicConnection);
113
- Init_quic_stream(rb_mQuic);
124
+ Init_quic_connection_client(rb_mQUICConnection);
125
+ Init_quic_stream(rb_mQUIC);
114
126
  }
data/ext/quic/quic.h CHANGED
@@ -6,38 +6,48 @@
6
6
 
7
7
  #include <ngtcp2/ngtcp2.h>
8
8
  #include <ngtcp2/ngtcp2_crypto.h>
9
- #include <ngtcp2/ngtcp2_crypto_quictls.h>
10
- #include <openssl/ssl.h>
11
-
12
- extern VALUE rb_mQuic;
13
- extern VALUE rb_mQuicConnection;
14
- extern VALUE rb_cQuicConnectionClient;
15
-
16
- extern VALUE rb_eQuicError;
17
- extern VALUE rb_eQuicErrorProto;
18
- extern VALUE rb_eQuicErrorDropConn;
19
- extern VALUE rb_eQuicErrorRetry;
20
- extern VALUE rb_eQuicErrorClosed;
21
- extern VALUE rb_eQuicErrorCryptoError;
22
- extern VALUE rb_eQuicErrorHandshakeTimeout;
23
- extern VALUE rb_eQuicErrorFlowControl;
24
- extern VALUE rb_eQuicErrorUnknown;
25
- extern VALUE rb_eQuicErrorWaitReadable;
26
- extern VALUE rb_eQuicErrorWaitWritable;
27
- extern VALUE rb_eQuicErrorStreamClosed;
28
- extern VALUE rb_eQuicErrorStreamReset;
29
- extern VALUE rb_eQuicErrorNotBound;
30
-
31
- void Init_quic_connection_client(VALUE rb_mQuicConnection);
9
+ #include <picotls.h>
10
+ #include <picotls/openssl.h>
11
+ #include <ngtcp2/ngtcp2_crypto_picotls.h>
12
+ #include <openssl/crypto.h>
13
+
14
+ /* picotls disables ptls_openssl_x25519 unless it is built with the patch in
15
+ ext/quic/patches/picotls/. extconf.rb checks the same condition at configure
16
+ time; this catches a prebuilt ports/ tree whose patch was swapped out. */
17
+ #if !PTLS_OPENSSL_HAVE_X25519
18
+ #error "picotls was built without X25519; is ext/quic/patches/picotls/*.patch applied?"
19
+ #endif
20
+
21
+ extern VALUE rb_mQUIC;
22
+ extern VALUE rb_mQUICConnection;
23
+ extern VALUE rb_cQUICConnectionClient;
24
+
25
+ extern VALUE rb_eQUICError;
26
+ extern VALUE rb_eQUICErrorProto;
27
+ extern VALUE rb_eQUICErrorDropConn;
28
+ extern VALUE rb_eQUICErrorRetry;
29
+ extern VALUE rb_eQUICErrorClosed;
30
+ extern VALUE rb_eQUICErrorCryptoError;
31
+ extern VALUE rb_eQUICErrorCertificateVerifyFailed;
32
+ extern VALUE rb_eQUICErrorHandshakeTimeout;
33
+ extern VALUE rb_eQUICErrorFlowControl;
34
+ extern VALUE rb_eQUICErrorUnknown;
35
+ extern VALUE rb_eQUICErrorWaitReadable;
36
+ extern VALUE rb_eQUICErrorWaitWritable;
37
+ extern VALUE rb_eQUICErrorStreamClosed;
38
+ extern VALUE rb_eQUICErrorStreamReset;
39
+ extern VALUE rb_eQUICErrorNotBound;
40
+
41
+ void Init_quic_connection_client(VALUE rb_mQUICConnection);
32
42
 
33
43
  /* Accessor used by stream.c to issue ngtcp2 calls (e.g.
34
44
  ngtcp2_conn_shutdown_stream_read) for streams that hold a back-reference
35
- to their parent Quic::Connection::Client via @client. Returns the raw
45
+ to their parent QUIC::Connection::Client via @client. Returns the raw
36
46
  ngtcp2_conn pointer; the caller is responsible for ensuring the Ruby
37
47
  Client object is alive for the duration of the call. */
38
48
  ngtcp2_conn *quic_client_conn(VALUE client);
39
49
 
40
- /* Raise a Quic::Error subclass mapped from an ngtcp2 negative error code.
50
+ /* Raise a QUIC::Error subclass mapped from an ngtcp2 negative error code.
41
51
  Never returns. */
42
52
  NORETURN(void quic_raise_ngtcp2_error(int rv));
43
53