quic 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.clang-format +1 -0
- data/.git-blame-ignore-revs +2 -0
- data/CHANGELOG.md +9 -0
- data/README.md +76 -2
- data/examples/demo_certificates.rb +67 -0
- data/examples/echo_endpoint_demo.rb +170 -0
- data/examples/echo_server_demo.rb +2 -58
- data/ext/quic/connection_client.c +77 -59
- data/ext/quic/connection_common.c +358 -365
- data/ext/quic/connection_common.h +20 -11
- data/ext/quic/connection_server.c +67 -48
- data/ext/quic/endpoint.c +206 -200
- data/ext/quic/endpoint.h +8 -5
- data/ext/quic/ext_api.c +41 -47
- data/ext/quic/include/quic/ext.h +26 -19
- data/ext/quic/quic.c +75 -67
- data/ext/quic/quic.h +4 -2
- data/ext/quic/stream.c +55 -74
- data/ext/quic/stream.h +5 -5
- data/ext/quic/tls_context.c +79 -70
- data/ext/quic/tls_context.h +2 -1
- data/lib/quic/version.rb +1 -1
- metadata +5 -1
|
@@ -38,7 +38,7 @@ typedef struct {
|
|
|
38
38
|
/* Filled by quic_override_verify_cb during the handshake so that a failed
|
|
39
39
|
read_pkt can raise CertificateVerifyFailed with the X509 error. */
|
|
40
40
|
bool verify_failed;
|
|
41
|
-
int verify_result;
|
|
41
|
+
int verify_result; /* X509_V_ERR_*, 0 when the server sent no certificate */
|
|
42
42
|
/* The TLS native handle handed to ngtcp2 (&cptls, not cptls.ptls). */
|
|
43
43
|
ngtcp2_crypto_picotls_ctx cptls;
|
|
44
44
|
/* [0]: QUIC transport params, filled in by ngtcp2; [1]: terminator. */
|
|
@@ -87,8 +87,9 @@ typedef struct {
|
|
|
87
87
|
recv_stream_data queues for #accept_stream. */
|
|
88
88
|
bool is_server;
|
|
89
89
|
/* <qlog_dir>/<SCID>.sqlog while ngtcp2 writes qlog to it, else NULL.
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
quic_conn_close_qlog closes it as soon as the connection ends;
|
|
91
|
+
NGTCP2_QLOG_WRITE_FLAG_FIN, sent by ngtcp2_conn_del, closes one still
|
|
92
|
+
open when the connection is freed. */
|
|
92
93
|
FILE *qlog;
|
|
93
94
|
/* The rest is for a connection a QUIC::Endpoint made, and stays zero (nil)
|
|
94
95
|
otherwise. endpoint is the Endpoint whose table holds this connection,
|
|
@@ -130,14 +131,14 @@ ngtcp2_tstamp quic_now(void);
|
|
|
130
131
|
*path. Returns the packet's length, 0 when there is nothing to send, or a
|
|
131
132
|
negative ngtcp2 error. Never raises for the connection's own failures: an
|
|
132
133
|
exception from a Ruby callback is left in c->pending_exc. */
|
|
133
|
-
ngtcp2_ssize quic_conn_write_pkt_raw(VALUE self, quic_conn_t *c,
|
|
134
|
-
uint8_t *dest);
|
|
134
|
+
ngtcp2_ssize quic_conn_write_pkt_raw(VALUE self, quic_conn_t *c,
|
|
135
|
+
ngtcp2_path *path, uint8_t *dest);
|
|
135
136
|
|
|
136
137
|
/* Feed one datagram to the connection, as #read_pkt does, and return what
|
|
137
138
|
ngtcp2_conn_read_pkt returned. Like quic_conn_write_pkt_raw, it leaves an
|
|
138
139
|
exception from a Ruby callback in c->pending_exc. */
|
|
139
|
-
int quic_conn_read_pkt_raw(quic_conn_t *c, const ngtcp2_path *path,
|
|
140
|
-
size_t datalen);
|
|
140
|
+
int quic_conn_read_pkt_raw(quic_conn_t *c, const ngtcp2_path *path,
|
|
141
|
+
const uint8_t *data, size_t datalen);
|
|
141
142
|
|
|
142
143
|
/* The exception #read_pkt raises for rv, an error ngtcp2_conn_read_pkt
|
|
143
144
|
returned: a CryptoError (or CertificateVerifyFailed) with the TLS alert
|
|
@@ -149,8 +150,9 @@ VALUE quic_conn_error_new(quic_conn_t *c, int rv);
|
|
|
149
150
|
(a QUIC::TLSContext). ep is the Endpoint that will hold it (its object
|
|
150
151
|
ep_owner), or NULL for Server.accept. Raises if ngtcp2 or picotls cannot
|
|
151
152
|
set the connection up. */
|
|
152
|
-
VALUE quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd,
|
|
153
|
-
|
|
153
|
+
VALUE quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd,
|
|
154
|
+
const ngtcp2_path *path, VALUE tls_context,
|
|
155
|
+
VALUE transport_params, VALUE settings_v,
|
|
154
156
|
quic_endpoint_t *ep, VALUE ep_owner);
|
|
155
157
|
|
|
156
158
|
quic_conn_t *quic_conn_get(VALUE self);
|
|
@@ -181,7 +183,8 @@ VALUE quic_path_value(VALUE value, const char *name);
|
|
|
181
183
|
the list's entries point into. Both are stored in *list and *buf as soon
|
|
182
184
|
as they are allocated, for the caller to release with xfree even when
|
|
183
185
|
this raises. An empty Array allocates nothing and sets *count to 0. */
|
|
184
|
-
void quic_copy_alpn(VALUE alpn_ary, ptls_iovec_t **list, size_t *count,
|
|
186
|
+
void quic_copy_alpn(VALUE alpn_ary, ptls_iovec_t **list, size_t *count,
|
|
187
|
+
uint8_t **buf);
|
|
185
188
|
|
|
186
189
|
/* Copy settings.alpn into memory owned by c (c->alpn / c->alpn_buf). An
|
|
187
190
|
empty list leaves c->alpn_count at 0. */
|
|
@@ -201,7 +204,13 @@ void quic_fill_settings(ngtcp2_settings *settings, VALUE st);
|
|
|
201
204
|
qlog there. c->scid must be set. A file that cannot be opened only goes
|
|
202
205
|
without qlog: QUIC::Settings#check_qlog_dir already caught a wrong
|
|
203
206
|
directory when the connection or Endpoint was set up. */
|
|
204
|
-
void quic_conn_open_qlog(quic_conn_t *c, VALUE settings_v,
|
|
207
|
+
void quic_conn_open_qlog(quic_conn_t *c, VALUE settings_v,
|
|
208
|
+
ngtcp2_settings *settings);
|
|
209
|
+
|
|
210
|
+
/* Close c's qlog file, if open. Called wherever the connection ends, so
|
|
211
|
+
that the file does not wait for GC; what ngtcp2 logs afterwards goes
|
|
212
|
+
nowhere. */
|
|
213
|
+
void quic_conn_close_qlog(quic_conn_t *c);
|
|
205
214
|
|
|
206
215
|
/* Zero *callbacks and fill in everything both endpoints share. The caller
|
|
207
216
|
adds the endpoint specific ones (client_initial / recv_retry, or
|
|
@@ -7,17 +7,15 @@ NORETURN(static void quic_server_raise_unacceptable(void));
|
|
|
7
7
|
|
|
8
8
|
/* The first datagram cannot start a connection. code stays nil: this is
|
|
9
9
|
_accept's own verdict, not an ngtcp2 error passed through. */
|
|
10
|
-
static void
|
|
11
|
-
quic_server_raise_unacceptable(void)
|
|
12
|
-
{
|
|
10
|
+
static void quic_server_raise_unacceptable(void) {
|
|
13
11
|
rb_raise(rb_eQUICErrorProto, "not an acceptable Initial packet");
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
VALUE
|
|
17
|
-
quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{
|
|
15
|
+
quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd,
|
|
16
|
+
const ngtcp2_path *path, VALUE tls_context,
|
|
17
|
+
VALUE transport_params, VALUE settings_v,
|
|
18
|
+
quic_endpoint_t *ep, VALUE ep_owner) {
|
|
21
19
|
VALUE self = quic_conn_alloc(klass);
|
|
22
20
|
quic_conn_t *c = quic_conn_get(self);
|
|
23
21
|
c->owner = self;
|
|
@@ -50,8 +48,10 @@ quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd, const ngtcp2_path *pa
|
|
|
50
48
|
/* Only an Endpoint can answer with a Stateless Reset, for a CID it no
|
|
51
49
|
longer knows, so only its connections announce a token. */
|
|
52
50
|
if (ep != NULL) {
|
|
53
|
-
if (quic_endpoint_generate_reset_token(ep, params.stateless_reset_token,
|
|
54
|
-
|
|
51
|
+
if (quic_endpoint_generate_reset_token(ep, params.stateless_reset_token,
|
|
52
|
+
&c->scid) != 0) {
|
|
53
|
+
rb_raise(rb_eRuntimeError,
|
|
54
|
+
"ngtcp2_crypto_generate_stateless_reset_token failed");
|
|
55
55
|
}
|
|
56
56
|
params.stateless_reset_token_present = 1;
|
|
57
57
|
}
|
|
@@ -66,19 +66,23 @@ quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd, const ngtcp2_path *pa
|
|
|
66
66
|
|
|
67
67
|
ngtcp2_settings settings;
|
|
68
68
|
quic_fill_settings(&settings, settings_v);
|
|
69
|
+
/* Every raise below closes it first. */
|
|
69
70
|
quic_conn_open_qlog(c, settings_v, &settings);
|
|
70
71
|
|
|
71
72
|
c->conn_ref.get_conn = quic_conn_get_conn;
|
|
72
73
|
c->conn_ref.user_data = c;
|
|
73
74
|
|
|
74
|
-
int rv =
|
|
75
|
-
|
|
75
|
+
int rv =
|
|
76
|
+
ngtcp2_conn_server_new(&c->conn, &c->dcid, &c->scid, path, hd->version,
|
|
77
|
+
&callbacks, &settings, ¶ms, NULL, c);
|
|
76
78
|
if (rv != 0) {
|
|
79
|
+
quic_conn_close_qlog(c);
|
|
77
80
|
quic_raise_ngtcp2_error(rv);
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
c->cptls.ptls = ptls_server_new(&c->tls->ctx);
|
|
81
84
|
if (c->cptls.ptls == NULL) {
|
|
85
|
+
quic_conn_close_qlog(c);
|
|
82
86
|
rb_raise(rb_eRuntimeError, "ptls_server_new failed");
|
|
83
87
|
}
|
|
84
88
|
/* Let ngtcp2's crypto callbacks resolve the ngtcp2_conn from the ptls_t. */
|
|
@@ -89,7 +93,9 @@ quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd, const ngtcp2_path *pa
|
|
|
89
93
|
c->tls_exts[1].type = UINT16_MAX;
|
|
90
94
|
c->cptls.handshake_properties.additional_extensions = c->tls_exts;
|
|
91
95
|
if (ngtcp2_crypto_picotls_configure_server_session(&c->cptls) != 0) {
|
|
92
|
-
|
|
96
|
+
quic_conn_close_qlog(c);
|
|
97
|
+
rb_raise(rb_eRuntimeError,
|
|
98
|
+
"ngtcp2_crypto_picotls_configure_server_session failed");
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
ngtcp2_conn_set_tls_native_handle(c->conn, &c->cptls);
|
|
@@ -105,35 +111,41 @@ quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd, const ngtcp2_path *pa
|
|
|
105
111
|
/* Build a server connection from the first datagram a client sent. Only the
|
|
106
112
|
ngtcp2_conn and the TLS session are set up here; QUIC::Connection::Server
|
|
107
113
|
.accept feeds that datagram to #read_pkt afterwards. */
|
|
108
|
-
static VALUE
|
|
109
|
-
quic_server_accept(int argc, VALUE *argv, VALUE klass)
|
|
110
|
-
{
|
|
114
|
+
static VALUE quic_server_accept(int argc, VALUE *argv, VALUE klass) {
|
|
111
115
|
VALUE opts = Qnil;
|
|
112
116
|
rb_scan_args(argc, argv, "0:", &opts);
|
|
113
117
|
if (NIL_P(opts)) {
|
|
114
|
-
rb_raise(
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
rb_raise(
|
|
119
|
+
rb_eArgError,
|
|
120
|
+
"missing keywords: initial_packet, local_sockaddr, remote_sockaddr, "
|
|
121
|
+
"certificate_path, private_key_path, transport_params, settings");
|
|
117
122
|
}
|
|
118
123
|
|
|
119
|
-
VALUE initial_packet
|
|
120
|
-
|
|
121
|
-
VALUE
|
|
122
|
-
|
|
123
|
-
VALUE
|
|
124
|
-
|
|
125
|
-
VALUE
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
VALUE initial_packet =
|
|
125
|
+
rb_hash_aref(opts, ID2SYM(rb_intern("initial_packet")));
|
|
126
|
+
VALUE local_sockaddr =
|
|
127
|
+
rb_hash_aref(opts, ID2SYM(rb_intern("local_sockaddr")));
|
|
128
|
+
VALUE remote_sockaddr =
|
|
129
|
+
rb_hash_aref(opts, ID2SYM(rb_intern("remote_sockaddr")));
|
|
130
|
+
VALUE certificate_path =
|
|
131
|
+
rb_hash_aref(opts, ID2SYM(rb_intern("certificate_path")));
|
|
132
|
+
VALUE private_key_path =
|
|
133
|
+
rb_hash_aref(opts, ID2SYM(rb_intern("private_key_path")));
|
|
134
|
+
VALUE transport_params =
|
|
135
|
+
rb_hash_aref(opts, ID2SYM(rb_intern("transport_params")));
|
|
136
|
+
VALUE settings_v = rb_hash_aref(opts, ID2SYM(rb_intern("settings")));
|
|
137
|
+
|
|
138
|
+
if (NIL_P(initial_packet) || NIL_P(local_sockaddr) ||
|
|
139
|
+
NIL_P(remote_sockaddr) || NIL_P(certificate_path) ||
|
|
140
|
+
NIL_P(private_key_path) || NIL_P(transport_params) || NIL_P(settings_v)) {
|
|
130
141
|
rb_raise(rb_eArgError,
|
|
131
|
-
"all keywords required: initial_packet, local_sockaddr,
|
|
142
|
+
"all keywords required: initial_packet, local_sockaddr, "
|
|
143
|
+
"remote_sockaddr, "
|
|
132
144
|
"certificate_path, private_key_path, transport_params, settings");
|
|
133
145
|
}
|
|
134
146
|
|
|
135
|
-
quic_require_binary(initial_packet,
|
|
136
|
-
quic_require_sockaddr(local_sockaddr,
|
|
147
|
+
quic_require_binary(initial_packet, "initial_packet");
|
|
148
|
+
quic_require_sockaddr(local_sockaddr, "local_sockaddr");
|
|
137
149
|
quic_require_sockaddr(remote_sockaddr, "remote_sockaddr");
|
|
138
150
|
certificate_path = quic_path_value(certificate_path, "certificate_path");
|
|
139
151
|
private_key_path = quic_path_value(private_key_path, "private_key_path");
|
|
@@ -143,44 +155,51 @@ quic_server_accept(int argc, VALUE *argv, VALUE klass)
|
|
|
143
155
|
|
|
144
156
|
/* ngtcp2_pkt_decode_version_cid asserts datalen > 0, and ngtcp2 is built
|
|
145
157
|
with assertions enabled, so an empty datagram would abort the process. */
|
|
146
|
-
if (datalen == 0)
|
|
158
|
+
if (datalen == 0)
|
|
159
|
+
quic_server_raise_unacceptable();
|
|
147
160
|
|
|
148
161
|
ngtcp2_version_cid vc;
|
|
149
162
|
int rv = ngtcp2_pkt_decode_version_cid(&vc, data, datalen, QUIC_SCID_LEN);
|
|
150
163
|
if (rv == NGTCP2_ERR_VERSION_NEGOTIATION) {
|
|
151
164
|
rb_raise(rb_eQUICErrorProto,
|
|
152
|
-
"unsupported QUIC version 0x%08x (Version Negotiation is not
|
|
165
|
+
"unsupported QUIC version 0x%08x (Version Negotiation is not "
|
|
166
|
+
"implemented)",
|
|
153
167
|
(unsigned int)vc.version);
|
|
154
168
|
}
|
|
155
|
-
if (rv != 0)
|
|
169
|
+
if (rv != 0)
|
|
170
|
+
quic_server_raise_unacceptable();
|
|
156
171
|
|
|
157
172
|
/* Unlike vc, hd holds the CIDs by value, so it outlives the packet buffer.
|
|
158
173
|
A token (address validation) is accepted without checking it: no Retry
|
|
159
174
|
is ever sent, so a well-behaved client does not send one. */
|
|
160
175
|
ngtcp2_pkt_hd hd;
|
|
161
|
-
if (ngtcp2_accept(&hd, data, datalen) != 0)
|
|
176
|
+
if (ngtcp2_accept(&hd, data, datalen) != 0)
|
|
177
|
+
quic_server_raise_unacceptable();
|
|
162
178
|
|
|
163
179
|
/* A context of this connection's own, as one made by
|
|
164
180
|
QUIC::TLSContext.server. verify_mode, ca_file and ca_path are not read,
|
|
165
181
|
since no client certificate is requested. */
|
|
166
|
-
VALUE tls_context =
|
|
167
|
-
|
|
182
|
+
VALUE tls_context =
|
|
183
|
+
quic_tls_context_server_new(certificate_path, private_key_path,
|
|
184
|
+
rb_funcall(settings_v, rb_intern("alpn"), 0));
|
|
168
185
|
|
|
169
186
|
ngtcp2_path path = {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
187
|
+
{(struct sockaddr *)RSTRING_PTR(local_sockaddr),
|
|
188
|
+
(ngtcp2_socklen)RSTRING_LEN(local_sockaddr)},
|
|
189
|
+
{(struct sockaddr *)RSTRING_PTR(remote_sockaddr),
|
|
190
|
+
(ngtcp2_socklen)RSTRING_LEN(remote_sockaddr)},
|
|
191
|
+
NULL,
|
|
173
192
|
};
|
|
174
193
|
|
|
175
|
-
return quic_server_conn_new(klass, &hd, &path, tls_context, transport_params,
|
|
176
|
-
NULL, Qnil);
|
|
194
|
+
return quic_server_conn_new(klass, &hd, &path, tls_context, transport_params,
|
|
195
|
+
settings_v, NULL, Qnil);
|
|
177
196
|
}
|
|
178
197
|
|
|
179
|
-
void
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
rb_cQUICConnectionServer = rb_define_class_under(rb_mQUICConnectionArg, "Server", rb_cObject);
|
|
198
|
+
void Init_quic_connection_server(VALUE rb_mQUICConnectionArg) {
|
|
199
|
+
rb_cQUICConnectionServer =
|
|
200
|
+
rb_define_class_under(rb_mQUICConnectionArg, "Server", rb_cObject);
|
|
183
201
|
rb_define_alloc_func(rb_cQUICConnectionServer, quic_conn_alloc);
|
|
184
|
-
rb_define_singleton_method(rb_cQUICConnectionServer, "_accept",
|
|
202
|
+
rb_define_singleton_method(rb_cQUICConnectionServer, "_accept",
|
|
203
|
+
quic_server_accept, -1);
|
|
185
204
|
quic_conn_define_shared_methods(rb_cQUICConnectionServer);
|
|
186
205
|
}
|