quic 0.0.2 → 0.0.3
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/CHANGELOG.md +24 -0
- data/LICENSE-DEPENDENCIES.txt +2 -19
- data/README.md +27 -2
- data/Rakefile +7 -0
- data/examples/echo_demo.rb +5 -6
- data/examples/echo_server_demo.rb +127 -0
- data/ext/quic/connection_client.c +23 -880
- data/ext/quic/connection_common.c +1262 -0
- data/ext/quic/connection_common.h +217 -0
- data/ext/quic/connection_server.c +186 -0
- data/ext/quic/endpoint.c +823 -0
- data/ext/quic/endpoint.h +28 -0
- data/ext/quic/ext_api.c +112 -0
- data/ext/quic/extconf.rb +12 -5
- data/ext/quic/include/quic/ext.h +84 -0
- data/ext/quic/quic.c +29 -4
- data/ext/quic/quic.h +8 -8
- data/ext/quic/stream.c +81 -20
- data/ext/quic/stream.h +13 -7
- data/ext/quic/tls_context.c +260 -0
- data/ext/quic/tls_context.h +46 -0
- data/lib/quic/connection.rb +142 -57
- data/lib/quic/endpoint.rb +57 -0
- data/lib/quic/ext_include.rb +12 -0
- data/lib/quic/settings.rb +26 -3
- data/lib/quic/stream.rb +4 -3
- data/lib/quic/version.rb +1 -1
- data/lib/quic.rb +2 -0
- data/sig/quic.rbs +203 -22
- metadata +13 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#ifndef QUIC_CONNECTION_COMMON_H
|
|
2
|
+
#define QUIC_CONNECTION_COMMON_H 1
|
|
3
|
+
|
|
4
|
+
#include <stdio.h>
|
|
5
|
+
|
|
6
|
+
#include "quic.h"
|
|
7
|
+
#include "quic/ext.h"
|
|
8
|
+
#include "tls_context.h"
|
|
9
|
+
|
|
10
|
+
/* Length of the source connection ID this endpoint picks for itself. */
|
|
11
|
+
#define QUIC_SCID_LEN 8
|
|
12
|
+
|
|
13
|
+
/* Buffer size for #write_pkt. NGTCP2_MAX_UDP_PAYLOAD_SIZE (1200) is the
|
|
14
|
+
minimum destlen ngtcp2 accepts. Revisit when PMTUD is enabled (Phase 4+). */
|
|
15
|
+
#define QUIC_WRITE_PKT_BUFLEN NGTCP2_MAX_UDP_PAYLOAD_SIZE
|
|
16
|
+
|
|
17
|
+
/* A QUIC::Endpoint (endpoint.c). */
|
|
18
|
+
typedef struct quic_endpoint quic_endpoint_t;
|
|
19
|
+
|
|
20
|
+
/* The C side of a QUIC::Connection::Client or QUIC::Connection::Server. Both
|
|
21
|
+
classes wrap it with quic_conn_data_type, so stream.c can reach the
|
|
22
|
+
ngtcp2_conn of either through quic_conn_ptr. Only the constructors
|
|
23
|
+
(_open / _accept) are class specific. */
|
|
24
|
+
typedef struct {
|
|
25
|
+
ngtcp2_conn *conn;
|
|
26
|
+
/* Client only. Referenced by cptls.ptls, which only keeps the pointer, so
|
|
27
|
+
this has to outlive the ptls_t. */
|
|
28
|
+
ptls_context_t tls_ctx;
|
|
29
|
+
/* Server only. The shared context cptls.ptls was made from, holding the
|
|
30
|
+
certificate chain, the signer and the ALPN list. This connection holds
|
|
31
|
+
a reference, released in quic_conn_free after the ptls_t. */
|
|
32
|
+
quic_tls_ctx_t *tls;
|
|
33
|
+
/* Client only. Referenced by tls_ctx.verify_certificate when verify_mode
|
|
34
|
+
is :peer. Holds a reference to an X509_STORE, released in
|
|
35
|
+
quic_conn_free. */
|
|
36
|
+
ptls_openssl_verify_certificate_t verify_cert;
|
|
37
|
+
bool verify_cert_initialized;
|
|
38
|
+
/* Filled by quic_override_verify_cb during the handshake so that a failed
|
|
39
|
+
read_pkt can raise CertificateVerifyFailed with the X509 error. */
|
|
40
|
+
bool verify_failed;
|
|
41
|
+
int verify_result; /* X509_V_ERR_*, 0 when the server sent no certificate */
|
|
42
|
+
/* The TLS native handle handed to ngtcp2 (&cptls, not cptls.ptls). */
|
|
43
|
+
ngtcp2_crypto_picotls_ctx cptls;
|
|
44
|
+
/* [0]: QUIC transport params, filled in by ngtcp2; [1]: terminator. */
|
|
45
|
+
ptls_raw_extension_t tls_exts[2];
|
|
46
|
+
/* Client only. ALPN list handed to picotls, which keeps the pointers for
|
|
47
|
+
the whole handshake (unlike SSL_set_alpn_protos, which copies). The
|
|
48
|
+
entries point into alpn_buf, and both are owned by this struct. */
|
|
49
|
+
ptls_iovec_t *alpn;
|
|
50
|
+
size_t alpn_count;
|
|
51
|
+
uint8_t *alpn_buf;
|
|
52
|
+
ngtcp2_cid scid;
|
|
53
|
+
ngtcp2_cid dcid;
|
|
54
|
+
/* ngtcp2 1.x requires applications to associate an ngtcp2_conn with the
|
|
55
|
+
TLS object so that crypto callbacks (e.g. add_handshake_data) can recover
|
|
56
|
+
the conn. picotls carries it in the ptls_t's data pointer, set via
|
|
57
|
+
ptls_get_data_ptr. The ref must outlive the ptls_t. */
|
|
58
|
+
ngtcp2_crypto_conn_ref conn_ref;
|
|
59
|
+
/* Back-reference to the Client or Server Ruby object that owns this
|
|
60
|
+
struct. ngtcp2 stream callbacks receive a void* user_data equal to this
|
|
61
|
+
struct, and they look up @streams via owner. We are stored INSIDE owner
|
|
62
|
+
via TypedData_Wrap_Struct, so owner is guaranteed alive while we exist
|
|
63
|
+
(no dmark needed). GC.compact may relocate owner, so quic_conn_compact
|
|
64
|
+
updates this field via rb_gc_location. */
|
|
65
|
+
VALUE owner;
|
|
66
|
+
/* An exception raised by Ruby code inside an ngtcp2 callback, caught with
|
|
67
|
+
rb_protect so that it does not unwind through ngtcp2's frames. #read_pkt,
|
|
68
|
+
#write_pkt and #handle_expiry re-raise it once ngtcp2 has returned.
|
|
69
|
+
Qnil when there is none. Marked by quic_conn_data_type's dmark. */
|
|
70
|
+
VALUE pending_exc;
|
|
71
|
+
/* The C stream handler registered through the quic_ext_api table
|
|
72
|
+
(set_stream_handler), or NULL. With a handler the connection makes no
|
|
73
|
+
QUIC::Stream objects: every stream callback goes to the handler, and
|
|
74
|
+
#write_pkt pulls stream data from it. handler_owner is marked while the
|
|
75
|
+
connection lives. */
|
|
76
|
+
const quic_ext_stream_handler *handler;
|
|
77
|
+
void *handler_ud;
|
|
78
|
+
VALUE handler_owner;
|
|
79
|
+
/* The time #read_pkt, #write_pkt or #handle_expiry read on entry. Handed
|
|
80
|
+
to the handler as `ts`, since ngtcp2 callbacks receive no time. */
|
|
81
|
+
ngtcp2_tstamp now;
|
|
82
|
+
/* The application error the handler left with set_application_error, to
|
|
83
|
+
close the connection with after it reports a failure. */
|
|
84
|
+
ngtcp2_ccerr close_error;
|
|
85
|
+
bool close_error_set;
|
|
86
|
+
/* Set by the constructor. Decides which peer-initiated streams
|
|
87
|
+
recv_stream_data queues for #accept_stream. */
|
|
88
|
+
bool is_server;
|
|
89
|
+
/* <qlog_dir>/<SCID>.sqlog while ngtcp2 writes qlog to it, else NULL.
|
|
90
|
+
Closed on NGTCP2_QLOG_WRITE_FLAG_FIN, which ngtcp2_conn_del sends at
|
|
91
|
+
the latest. */
|
|
92
|
+
FILE *qlog;
|
|
93
|
+
/* The rest is for a connection a QUIC::Endpoint made, and stays zero (nil)
|
|
94
|
+
otherwise. endpoint is the Endpoint whose table holds this connection,
|
|
95
|
+
NULL once it has left the table; endpoint_owner is that Endpoint's
|
|
96
|
+
object, marked so that it outlives the connections it holds. */
|
|
97
|
+
bool from_endpoint;
|
|
98
|
+
quic_endpoint_t *endpoint;
|
|
99
|
+
VALUE endpoint_owner;
|
|
100
|
+
/* Why the Endpoint closed the connection (Connection#error): nil for a
|
|
101
|
+
close by the peer, by idle timeout or by #close. */
|
|
102
|
+
VALUE error;
|
|
103
|
+
/* When the closing or draining period ends; 0 while the connection is
|
|
104
|
+
open. */
|
|
105
|
+
ngtcp2_tstamp close_deadline;
|
|
106
|
+
/* The CONNECTION_CLOSE packet built on entering the closing period, sent
|
|
107
|
+
again for packets the peer keeps sending, at most three times what they
|
|
108
|
+
add up to and with exponential backoff (examples/server.cc). */
|
|
109
|
+
VALUE closebuf;
|
|
110
|
+
uint64_t close_bytes_recv;
|
|
111
|
+
uint64_t close_bytes_sent;
|
|
112
|
+
uint64_t close_pkts_recv;
|
|
113
|
+
uint64_t close_next_pkts_recv;
|
|
114
|
+
} quic_conn_t;
|
|
115
|
+
|
|
116
|
+
extern const rb_data_type_t quic_conn_data_type;
|
|
117
|
+
|
|
118
|
+
extern ptls_key_exchange_algorithm_t *quic_key_exchanges[];
|
|
119
|
+
extern ptls_cipher_suite_t *quic_cipher_suites[];
|
|
120
|
+
|
|
121
|
+
/* Allocator for both connection classes. The struct starts zeroed, except
|
|
122
|
+
its VALUE fields, which start as Qnil. */
|
|
123
|
+
VALUE quic_conn_alloc(VALUE klass);
|
|
124
|
+
|
|
125
|
+
/* CLOCK_MONOTONIC in ngtcp2's units (nanoseconds). */
|
|
126
|
+
ngtcp2_tstamp quic_now(void);
|
|
127
|
+
|
|
128
|
+
/* Build the next packet of the connection self (wrapping c) into dest of
|
|
129
|
+
QUIC_WRITE_PKT_BUFLEN bytes, as #write_pkt does, and store its path in
|
|
130
|
+
*path. Returns the packet's length, 0 when there is nothing to send, or a
|
|
131
|
+
negative ngtcp2 error. Never raises for the connection's own failures: an
|
|
132
|
+
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, ngtcp2_path *path,
|
|
134
|
+
uint8_t *dest);
|
|
135
|
+
|
|
136
|
+
/* Feed one datagram to the connection, as #read_pkt does, and return what
|
|
137
|
+
ngtcp2_conn_read_pkt returned. Like quic_conn_write_pkt_raw, it leaves an
|
|
138
|
+
exception from a Ruby callback in c->pending_exc. */
|
|
139
|
+
int quic_conn_read_pkt_raw(quic_conn_t *c, const ngtcp2_path *path, const uint8_t *data,
|
|
140
|
+
size_t datalen);
|
|
141
|
+
|
|
142
|
+
/* The exception #read_pkt raises for rv, an error ngtcp2_conn_read_pkt
|
|
143
|
+
returned: a CryptoError (or CertificateVerifyFailed) with the TLS alert
|
|
144
|
+
for NGTCP2_ERR_CRYPTO, quic_ngtcp2_error_new(rv) otherwise. */
|
|
145
|
+
VALUE quic_conn_error_new(quic_conn_t *c, int rv);
|
|
146
|
+
|
|
147
|
+
/* Build a QUIC::Connection::Server of class klass for the client whose
|
|
148
|
+
first Initial is hd, received on path, with the TLS context tls_context
|
|
149
|
+
(a QUIC::TLSContext). ep is the Endpoint that will hold it (its object
|
|
150
|
+
ep_owner), or NULL for Server.accept. Raises if ngtcp2 or picotls cannot
|
|
151
|
+
set the connection up. */
|
|
152
|
+
VALUE quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd, const ngtcp2_path *path,
|
|
153
|
+
VALUE tls_context, VALUE transport_params, VALUE settings_v,
|
|
154
|
+
quic_endpoint_t *ep, VALUE ep_owner);
|
|
155
|
+
|
|
156
|
+
quic_conn_t *quic_conn_get(VALUE self);
|
|
157
|
+
|
|
158
|
+
/* The raw ngtcp2_conn of a Client or Server, for stream.c to issue ngtcp2
|
|
159
|
+
calls (e.g. ngtcp2_conn_shutdown_stream_read) on streams whose @client is
|
|
160
|
+
that connection. The caller must keep the connection object alive for the
|
|
161
|
+
duration of the call. */
|
|
162
|
+
ngtcp2_conn *quic_conn_ptr(VALUE owner);
|
|
163
|
+
|
|
164
|
+
/* ngtcp2_crypto_conn_ref.get_conn for quic_conn_t. */
|
|
165
|
+
ngtcp2_conn *quic_conn_get_conn(ngtcp2_crypto_conn_ref *conn_ref);
|
|
166
|
+
|
|
167
|
+
void quic_require_binary(VALUE str, const char *name);
|
|
168
|
+
|
|
169
|
+
/* str must be a binary String holding an AF_INET or AF_INET6 sockaddr of
|
|
170
|
+
exactly that family's size, as Addrinfo#to_sockaddr makes. ngtcp2 copies
|
|
171
|
+
an address by its length into storage only as large as sockaddr_in6, so
|
|
172
|
+
anything longer would overrun it. Raises ArgumentError otherwise. */
|
|
173
|
+
void quic_require_sockaddr(VALUE str, const char *name);
|
|
174
|
+
|
|
175
|
+
/* Validate a path argument and return it as a String, or Qnil for nil.
|
|
176
|
+
Accepts a String or anything responding to #to_path, and raises
|
|
177
|
+
ArgumentError for any other type or a path containing NUL. */
|
|
178
|
+
VALUE quic_path_value(VALUE value, const char *name);
|
|
179
|
+
|
|
180
|
+
/* Copy alpn_ary, an Array of 1-255 byte Strings, into a list and a buffer
|
|
181
|
+
the list's entries point into. Both are stored in *list and *buf as soon
|
|
182
|
+
as they are allocated, for the caller to release with xfree even when
|
|
183
|
+
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, uint8_t **buf);
|
|
185
|
+
|
|
186
|
+
/* Copy settings.alpn into memory owned by c (c->alpn / c->alpn_buf). An
|
|
187
|
+
empty list leaves c->alpn_count at 0. */
|
|
188
|
+
void quic_conn_copy_alpn(quic_conn_t *c, VALUE settings_v);
|
|
189
|
+
|
|
190
|
+
/* Give the peer back the stream stream_id used up, if it is a
|
|
191
|
+
bidirectional stream the peer opened, as ngtcp2's examples/server.cc
|
|
192
|
+
does when such a stream closes. Without this the peer can open only
|
|
193
|
+
initial_max_streams_bidi streams over the whole connection. */
|
|
194
|
+
void quic_conn_give_back_peer_stream(ngtcp2_conn *conn, int64_t stream_id);
|
|
195
|
+
|
|
196
|
+
void quic_fill_transport_params(ngtcp2_transport_params *params, VALUE tp);
|
|
197
|
+
void quic_fill_settings(ngtcp2_settings *settings, VALUE st);
|
|
198
|
+
|
|
199
|
+
/* When settings_v.qlog_dir is set, open <qlog_dir>/<c->scid in hex>.sqlog
|
|
200
|
+
(as ngtcp2's examples name it) and have ngtcp2 write the connection's
|
|
201
|
+
qlog there. c->scid must be set. A file that cannot be opened only goes
|
|
202
|
+
without qlog: QUIC::Settings#check_qlog_dir already caught a wrong
|
|
203
|
+
directory when the connection or Endpoint was set up. */
|
|
204
|
+
void quic_conn_open_qlog(quic_conn_t *c, VALUE settings_v, ngtcp2_settings *settings);
|
|
205
|
+
|
|
206
|
+
/* Zero *callbacks and fill in everything both endpoints share. The caller
|
|
207
|
+
adds the endpoint specific ones (client_initial / recv_retry, or
|
|
208
|
+
recv_client_initial). */
|
|
209
|
+
void quic_conn_fill_common_callbacks(ngtcp2_callbacks *callbacks);
|
|
210
|
+
|
|
211
|
+
/* Define the packet I/O, timer, state and stream methods on klass. */
|
|
212
|
+
void quic_conn_define_shared_methods(VALUE klass);
|
|
213
|
+
|
|
214
|
+
/* The quic_ext_api table (ext_api.c) that quic_ext_get_api hands out. */
|
|
215
|
+
extern const quic_ext_api quic_ext_api_table;
|
|
216
|
+
|
|
217
|
+
#endif /* QUIC_CONNECTION_COMMON_H */
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#include "connection_common.h"
|
|
2
|
+
#include "endpoint.h"
|
|
3
|
+
|
|
4
|
+
#include <openssl/rand.h>
|
|
5
|
+
|
|
6
|
+
NORETURN(static void quic_server_raise_unacceptable(void));
|
|
7
|
+
|
|
8
|
+
/* The first datagram cannot start a connection. code stays nil: this is
|
|
9
|
+
_accept's own verdict, not an ngtcp2 error passed through. */
|
|
10
|
+
static void
|
|
11
|
+
quic_server_raise_unacceptable(void)
|
|
12
|
+
{
|
|
13
|
+
rb_raise(rb_eQUICErrorProto, "not an acceptable Initial packet");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
VALUE
|
|
17
|
+
quic_server_conn_new(VALUE klass, const ngtcp2_pkt_hd *hd, const ngtcp2_path *path,
|
|
18
|
+
VALUE tls_context, VALUE transport_params, VALUE settings_v,
|
|
19
|
+
quic_endpoint_t *ep, VALUE ep_owner)
|
|
20
|
+
{
|
|
21
|
+
VALUE self = quic_conn_alloc(klass);
|
|
22
|
+
quic_conn_t *c = quic_conn_get(self);
|
|
23
|
+
c->owner = self;
|
|
24
|
+
c->is_server = true;
|
|
25
|
+
/* Set before ngtcp2_conn_server_new, whose callbacks may already look for
|
|
26
|
+
the Endpoint. */
|
|
27
|
+
if (ep != NULL) {
|
|
28
|
+
c->from_endpoint = true;
|
|
29
|
+
c->endpoint = ep;
|
|
30
|
+
c->endpoint_owner = ep_owner;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* The reference taken here keeps the context alive until quic_conn_free,
|
|
34
|
+
whatever happens to the QUIC::TLSContext object. */
|
|
35
|
+
c->tls = quic_tls_context_get(tls_context);
|
|
36
|
+
quic_tls_ctx_ref(c->tls);
|
|
37
|
+
|
|
38
|
+
c->dcid = hd->scid;
|
|
39
|
+
c->scid.datalen = QUIC_SCID_LEN;
|
|
40
|
+
if (RAND_bytes(c->scid.data, QUIC_SCID_LEN) != 1) {
|
|
41
|
+
rb_raise(rb_eRuntimeError, "RAND_bytes(scid) failed");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
ngtcp2_transport_params params;
|
|
45
|
+
quic_fill_transport_params(¶ms, transport_params);
|
|
46
|
+
/* Without Retry, the original DCID is the one in this first packet. The
|
|
47
|
+
handshake fails if it is missing. */
|
|
48
|
+
params.original_dcid = hd->dcid;
|
|
49
|
+
params.original_dcid_present = 1;
|
|
50
|
+
/* Only an Endpoint can answer with a Stateless Reset, for a CID it no
|
|
51
|
+
longer knows, so only its connections announce a token. */
|
|
52
|
+
if (ep != NULL) {
|
|
53
|
+
if (quic_endpoint_generate_reset_token(ep, params.stateless_reset_token, &c->scid) != 0) {
|
|
54
|
+
rb_raise(rb_eRuntimeError, "ngtcp2_crypto_generate_stateless_reset_token failed");
|
|
55
|
+
}
|
|
56
|
+
params.stateless_reset_token_present = 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* quic_conn_alloc already zeroed the struct, but calling ctx_init keeps
|
|
60
|
+
the ngtcp2-side initialization explicit, as in _open. */
|
|
61
|
+
ngtcp2_crypto_picotls_ctx_init(&c->cptls);
|
|
62
|
+
|
|
63
|
+
ngtcp2_callbacks callbacks;
|
|
64
|
+
quic_conn_fill_common_callbacks(&callbacks);
|
|
65
|
+
callbacks.recv_client_initial = ngtcp2_crypto_recv_client_initial_cb;
|
|
66
|
+
|
|
67
|
+
ngtcp2_settings settings;
|
|
68
|
+
quic_fill_settings(&settings, settings_v);
|
|
69
|
+
quic_conn_open_qlog(c, settings_v, &settings);
|
|
70
|
+
|
|
71
|
+
c->conn_ref.get_conn = quic_conn_get_conn;
|
|
72
|
+
c->conn_ref.user_data = c;
|
|
73
|
+
|
|
74
|
+
int rv = ngtcp2_conn_server_new(&c->conn, &c->dcid, &c->scid, path, hd->version,
|
|
75
|
+
&callbacks, &settings, ¶ms, NULL, c);
|
|
76
|
+
if (rv != 0) {
|
|
77
|
+
quic_raise_ngtcp2_error(rv);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
c->cptls.ptls = ptls_server_new(&c->tls->ctx);
|
|
81
|
+
if (c->cptls.ptls == NULL) {
|
|
82
|
+
rb_raise(rb_eRuntimeError, "ptls_server_new failed");
|
|
83
|
+
}
|
|
84
|
+
/* Let ngtcp2's crypto callbacks resolve the ngtcp2_conn from the ptls_t. */
|
|
85
|
+
*ptls_get_data_ptr(c->cptls.ptls) = &c->conn_ref;
|
|
86
|
+
/* [0] carries the QUIC transport parameters, filled in by ngtcp2; [1]
|
|
87
|
+
terminates the list. */
|
|
88
|
+
c->tls_exts[0].type = UINT16_MAX;
|
|
89
|
+
c->tls_exts[1].type = UINT16_MAX;
|
|
90
|
+
c->cptls.handshake_properties.additional_extensions = c->tls_exts;
|
|
91
|
+
if (ngtcp2_crypto_picotls_configure_server_session(&c->cptls) != 0) {
|
|
92
|
+
rb_raise(rb_eRuntimeError, "ngtcp2_crypto_picotls_configure_server_session failed");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
ngtcp2_conn_set_tls_native_handle(c->conn, &c->cptls);
|
|
96
|
+
|
|
97
|
+
/* Stream registry and accept queue, as in _open. The accept queue holds
|
|
98
|
+
client-initiated streams here. */
|
|
99
|
+
rb_ivar_set(self, rb_intern("@streams"), rb_hash_new());
|
|
100
|
+
rb_ivar_set(self, rb_intern("@accept_queue"), rb_ary_new());
|
|
101
|
+
|
|
102
|
+
return self;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Build a server connection from the first datagram a client sent. Only the
|
|
106
|
+
ngtcp2_conn and the TLS session are set up here; QUIC::Connection::Server
|
|
107
|
+
.accept feeds that datagram to #read_pkt afterwards. */
|
|
108
|
+
static VALUE
|
|
109
|
+
quic_server_accept(int argc, VALUE *argv, VALUE klass)
|
|
110
|
+
{
|
|
111
|
+
VALUE opts = Qnil;
|
|
112
|
+
rb_scan_args(argc, argv, "0:", &opts);
|
|
113
|
+
if (NIL_P(opts)) {
|
|
114
|
+
rb_raise(rb_eArgError,
|
|
115
|
+
"missing keywords: initial_packet, local_sockaddr, remote_sockaddr, "
|
|
116
|
+
"certificate_path, private_key_path, transport_params, settings");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
VALUE initial_packet = rb_hash_aref(opts, ID2SYM(rb_intern("initial_packet")));
|
|
120
|
+
VALUE local_sockaddr = rb_hash_aref(opts, ID2SYM(rb_intern("local_sockaddr")));
|
|
121
|
+
VALUE remote_sockaddr = rb_hash_aref(opts, ID2SYM(rb_intern("remote_sockaddr")));
|
|
122
|
+
VALUE certificate_path = rb_hash_aref(opts, ID2SYM(rb_intern("certificate_path")));
|
|
123
|
+
VALUE private_key_path = rb_hash_aref(opts, ID2SYM(rb_intern("private_key_path")));
|
|
124
|
+
VALUE transport_params = rb_hash_aref(opts, ID2SYM(rb_intern("transport_params")));
|
|
125
|
+
VALUE settings_v = rb_hash_aref(opts, ID2SYM(rb_intern("settings")));
|
|
126
|
+
|
|
127
|
+
if (NIL_P(initial_packet) || NIL_P(local_sockaddr) || NIL_P(remote_sockaddr) ||
|
|
128
|
+
NIL_P(certificate_path) || NIL_P(private_key_path) ||
|
|
129
|
+
NIL_P(transport_params) || NIL_P(settings_v)) {
|
|
130
|
+
rb_raise(rb_eArgError,
|
|
131
|
+
"all keywords required: initial_packet, local_sockaddr, remote_sockaddr, "
|
|
132
|
+
"certificate_path, private_key_path, transport_params, settings");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
quic_require_binary(initial_packet, "initial_packet");
|
|
136
|
+
quic_require_sockaddr(local_sockaddr, "local_sockaddr");
|
|
137
|
+
quic_require_sockaddr(remote_sockaddr, "remote_sockaddr");
|
|
138
|
+
certificate_path = quic_path_value(certificate_path, "certificate_path");
|
|
139
|
+
private_key_path = quic_path_value(private_key_path, "private_key_path");
|
|
140
|
+
|
|
141
|
+
const uint8_t *data = (const uint8_t *)RSTRING_PTR(initial_packet);
|
|
142
|
+
size_t datalen = (size_t)RSTRING_LEN(initial_packet);
|
|
143
|
+
|
|
144
|
+
/* ngtcp2_pkt_decode_version_cid asserts datalen > 0, and ngtcp2 is built
|
|
145
|
+
with assertions enabled, so an empty datagram would abort the process. */
|
|
146
|
+
if (datalen == 0) quic_server_raise_unacceptable();
|
|
147
|
+
|
|
148
|
+
ngtcp2_version_cid vc;
|
|
149
|
+
int rv = ngtcp2_pkt_decode_version_cid(&vc, data, datalen, QUIC_SCID_LEN);
|
|
150
|
+
if (rv == NGTCP2_ERR_VERSION_NEGOTIATION) {
|
|
151
|
+
rb_raise(rb_eQUICErrorProto,
|
|
152
|
+
"unsupported QUIC version 0x%08x (Version Negotiation is not implemented)",
|
|
153
|
+
(unsigned int)vc.version);
|
|
154
|
+
}
|
|
155
|
+
if (rv != 0) quic_server_raise_unacceptable();
|
|
156
|
+
|
|
157
|
+
/* Unlike vc, hd holds the CIDs by value, so it outlives the packet buffer.
|
|
158
|
+
A token (address validation) is accepted without checking it: no Retry
|
|
159
|
+
is ever sent, so a well-behaved client does not send one. */
|
|
160
|
+
ngtcp2_pkt_hd hd;
|
|
161
|
+
if (ngtcp2_accept(&hd, data, datalen) != 0) quic_server_raise_unacceptable();
|
|
162
|
+
|
|
163
|
+
/* A context of this connection's own, as one made by
|
|
164
|
+
QUIC::TLSContext.server. verify_mode, ca_file and ca_path are not read,
|
|
165
|
+
since no client certificate is requested. */
|
|
166
|
+
VALUE tls_context = quic_tls_context_server_new(
|
|
167
|
+
certificate_path, private_key_path, rb_funcall(settings_v, rb_intern("alpn"), 0));
|
|
168
|
+
|
|
169
|
+
ngtcp2_path path = {
|
|
170
|
+
{(struct sockaddr *)RSTRING_PTR(local_sockaddr), (ngtcp2_socklen)RSTRING_LEN(local_sockaddr)},
|
|
171
|
+
{(struct sockaddr *)RSTRING_PTR(remote_sockaddr), (ngtcp2_socklen)RSTRING_LEN(remote_sockaddr)},
|
|
172
|
+
NULL,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
return quic_server_conn_new(klass, &hd, &path, tls_context, transport_params, settings_v,
|
|
176
|
+
NULL, Qnil);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
void
|
|
180
|
+
Init_quic_connection_server(VALUE rb_mQUICConnectionArg)
|
|
181
|
+
{
|
|
182
|
+
rb_cQUICConnectionServer = rb_define_class_under(rb_mQUICConnectionArg, "Server", rb_cObject);
|
|
183
|
+
rb_define_alloc_func(rb_cQUICConnectionServer, quic_conn_alloc);
|
|
184
|
+
rb_define_singleton_method(rb_cQUICConnectionServer, "_accept", quic_server_accept, -1);
|
|
185
|
+
quic_conn_define_shared_methods(rb_cQUICConnectionServer);
|
|
186
|
+
}
|