| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* |
|
2
|
|
|
|
|
|
|
* feersum_tls.c.inc - TLS 1.3 support via picotls for Feersum |
|
3
|
|
|
|
|
|
|
* |
|
4
|
|
|
|
|
|
|
* This file is #included into Feersum.xs when FEERSUM_HAS_TLS is defined. |
|
5
|
|
|
|
|
|
|
* It provides separate read/write callbacks for TLS connections so that |
|
6
|
|
|
|
|
|
|
* plain HTTP connections are never disturbed. |
|
7
|
|
|
|
|
|
|
* |
|
8
|
|
|
|
|
|
|
* Flow: |
|
9
|
|
|
|
|
|
|
* Plain: accept -> try_conn_read/write (unchanged) |
|
10
|
|
|
|
|
|
|
* TLS+H1: accept -> try_tls_conn_read/write -> decrypt -> H1 parser |
|
11
|
|
|
|
|
|
|
* TLS+H2: accept -> try_tls_conn_read/write -> decrypt -> nghttp2 |
|
12
|
|
|
|
|
|
|
*/ |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_TLS |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
/* ALPN protocols list for negotiation */ |
|
17
|
|
|
|
|
|
|
static ptls_iovec_t tls_alpn_protos[] = { |
|
18
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_H2 |
|
19
|
|
|
|
|
|
|
{ (uint8_t *)ALPN_H2 + 1, 2 }, /* "h2" */ |
|
20
|
|
|
|
|
|
|
#endif |
|
21
|
|
|
|
|
|
|
{ (uint8_t *)ALPN_HTTP11 + 1, 8 }, /* "http/1.1" */ |
|
22
|
|
|
|
|
|
|
}; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
/* HTTP/1.1-only ALPN — used when h2 is not enabled on a listener */ |
|
25
|
|
|
|
|
|
|
static ptls_iovec_t tls_alpn_h1_only[] = { |
|
26
|
|
|
|
|
|
|
{ (uint8_t *)ALPN_HTTP11 + 1, 8 }, /* "http/1.1" */ |
|
27
|
|
|
|
|
|
|
}; |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
static int |
|
30
|
42
|
|
|
|
|
|
negotiate_alpn(ptls_t *tls, const ptls_iovec_t *protos, size_t num_protos, |
|
31
|
|
|
|
|
|
|
ptls_on_client_hello_parameters_t *params) |
|
32
|
|
|
|
|
|
|
{ |
|
33
|
|
|
|
|
|
|
size_t i, j; |
|
34
|
84
|
100
|
|
|
|
|
for (i = 0; i < num_protos; i++) { |
|
35
|
42
|
50
|
|
|
|
|
for (j = 0; j < params->negotiated_protocols.count; j++) { |
|
36
|
0
|
0
|
|
|
|
|
if (params->negotiated_protocols.list[j].len == protos[i].len && |
|
37
|
0
|
|
|
|
|
|
memcmp(params->negotiated_protocols.list[j].base, protos[i].base, |
|
38
|
0
|
0
|
|
|
|
|
protos[i].len) == 0) { |
|
39
|
0
|
|
|
|
|
|
ptls_set_negotiated_protocol(tls, |
|
40
|
0
|
|
|
|
|
|
(const char *)protos[i].base, protos[i].len); |
|
41
|
0
|
|
|
|
|
|
return 0; |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
} |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
/* No matching protocol; proceed without ALPN (will default to HTTP/1.1) */ |
|
46
|
42
|
|
|
|
|
|
return 0; |
|
47
|
|
|
|
|
|
|
} |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
static int |
|
50
|
0
|
|
|
|
|
|
on_client_hello_cb(ptls_on_client_hello_t *self, ptls_t *tls, |
|
51
|
|
|
|
|
|
|
ptls_on_client_hello_parameters_t *params) |
|
52
|
|
|
|
|
|
|
{ |
|
53
|
|
|
|
|
|
|
PERL_UNUSED_VAR(self); |
|
54
|
0
|
|
|
|
|
|
return negotiate_alpn(tls, tls_alpn_protos, |
|
55
|
|
|
|
|
|
|
sizeof(tls_alpn_protos) / sizeof(tls_alpn_protos[0]), params); |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
static ptls_on_client_hello_t on_client_hello = { on_client_hello_cb }; |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
static int |
|
61
|
42
|
|
|
|
|
|
on_client_hello_no_h2_cb(ptls_on_client_hello_t *self, ptls_t *tls, |
|
62
|
|
|
|
|
|
|
ptls_on_client_hello_parameters_t *params) |
|
63
|
|
|
|
|
|
|
{ |
|
64
|
|
|
|
|
|
|
PERL_UNUSED_VAR(self); |
|
65
|
42
|
|
|
|
|
|
return negotiate_alpn(tls, tls_alpn_h1_only, |
|
66
|
|
|
|
|
|
|
sizeof(tls_alpn_h1_only) / sizeof(tls_alpn_h1_only[0]), params); |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
static ptls_on_client_hello_t on_client_hello_no_h2 = { on_client_hello_no_h2_cb }; |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
static ptls_key_exchange_algorithm_t *feer_tls_key_exchanges[] = { |
|
72
|
|
|
|
|
|
|
#if PTLS_OPENSSL_HAVE_X25519 |
|
73
|
|
|
|
|
|
|
&ptls_openssl_x25519, |
|
74
|
|
|
|
|
|
|
#endif |
|
75
|
|
|
|
|
|
|
&ptls_openssl_secp256r1, |
|
76
|
|
|
|
|
|
|
NULL |
|
77
|
|
|
|
|
|
|
}; |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
static ptls_cipher_suite_t *feer_tls_cipher_suites[] = { |
|
80
|
|
|
|
|
|
|
&ptls_openssl_aes256gcmsha384, |
|
81
|
|
|
|
|
|
|
&ptls_openssl_aes128gcmsha256, |
|
82
|
|
|
|
|
|
|
#if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 |
|
83
|
|
|
|
|
|
|
&ptls_openssl_chacha20poly1305sha256, |
|
84
|
|
|
|
|
|
|
#endif |
|
85
|
|
|
|
|
|
|
NULL |
|
86
|
|
|
|
|
|
|
}; |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
/* |
|
89
|
|
|
|
|
|
|
* Create a picotls server context from certificate and key files. |
|
90
|
|
|
|
|
|
|
* Returns NULL on failure (with warnings emitted). |
|
91
|
|
|
|
|
|
|
*/ |
|
92
|
|
|
|
|
|
|
static ptls_context_t * |
|
93
|
73
|
|
|
|
|
|
feer_tls_create_context(pTHX_ const char *cert_file, const char *key_file, int h2) |
|
94
|
|
|
|
|
|
|
{ |
|
95
|
|
|
|
|
|
|
ptls_context_t *ctx; |
|
96
|
|
|
|
|
|
|
FILE *fp; |
|
97
|
|
|
|
|
|
|
int ret; |
|
98
|
|
|
|
|
|
|
|
|
99
|
73
|
|
|
|
|
|
Newxz(ctx, 1, ptls_context_t); |
|
100
|
|
|
|
|
|
|
|
|
101
|
73
|
|
|
|
|
|
ctx->random_bytes = ptls_openssl_random_bytes; |
|
102
|
73
|
|
|
|
|
|
ctx->get_time = &ptls_get_time; |
|
103
|
73
|
|
|
|
|
|
ctx->key_exchanges = feer_tls_key_exchanges; |
|
104
|
73
|
|
|
|
|
|
ctx->cipher_suites = feer_tls_cipher_suites; |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
/* Load certificate chain */ |
|
107
|
73
|
|
|
|
|
|
ret = ptls_load_certificates(ctx, cert_file); |
|
108
|
73
|
100
|
|
|
|
|
if (ret != 0) { |
|
109
|
3
|
|
|
|
|
|
warn("Feersum TLS: failed to load certificate from '%s' (error %d)\n", |
|
110
|
|
|
|
|
|
|
cert_file, ret); |
|
111
|
3
|
|
|
|
|
|
feer_tls_free_context(ctx); |
|
112
|
3
|
|
|
|
|
|
return NULL; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
/* Load private key */ |
|
116
|
70
|
|
|
|
|
|
fp = fopen(key_file, "r"); |
|
117
|
70
|
100
|
|
|
|
|
if (!fp) { |
|
118
|
2
|
|
|
|
|
|
warn("Feersum TLS: failed to open key file '%s': %s\n", |
|
119
|
|
|
|
|
|
|
key_file, strerror(errno)); |
|
120
|
2
|
|
|
|
|
|
goto cert_cleanup; |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
|
|
123
|
68
|
|
|
|
|
|
EVP_PKEY *pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL); |
|
124
|
68
|
|
|
|
|
|
fclose(fp); |
|
125
|
68
|
50
|
|
|
|
|
if (!pkey) { |
|
126
|
0
|
|
|
|
|
|
warn("Feersum TLS: failed to read private key from '%s'\n", key_file); |
|
127
|
0
|
|
|
|
|
|
goto cert_cleanup; |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
ptls_openssl_sign_certificate_t *sign_cert; |
|
131
|
68
|
|
|
|
|
|
Newx(sign_cert, 1, ptls_openssl_sign_certificate_t); |
|
132
|
68
|
50
|
|
|
|
|
if (ptls_openssl_init_sign_certificate(sign_cert, pkey) != 0) { |
|
133
|
0
|
|
|
|
|
|
Safefree(sign_cert); |
|
134
|
0
|
|
|
|
|
|
EVP_PKEY_free(pkey); |
|
135
|
0
|
|
|
|
|
|
warn("Feersum TLS: incompatible private key type in '%s'\n", key_file); |
|
136
|
0
|
|
|
|
|
|
goto cert_cleanup; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
68
|
|
|
|
|
|
EVP_PKEY_free(pkey); |
|
139
|
68
|
|
|
|
|
|
ctx->sign_certificate = &sign_cert->super; |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
/* ALPN negotiation via on_client_hello callback */ |
|
142
|
68
|
50
|
|
|
|
|
if (h2) |
|
143
|
0
|
|
|
|
|
|
ctx->on_client_hello = &on_client_hello; |
|
144
|
|
|
|
|
|
|
else |
|
145
|
68
|
|
|
|
|
|
ctx->on_client_hello = &on_client_hello_no_h2; |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
trace("TLS context created: cert=%s key=%s h2=%d\n", cert_file, key_file, h2); |
|
148
|
68
|
|
|
|
|
|
return ctx; |
|
149
|
|
|
|
|
|
|
|
|
150
|
2
|
|
|
|
|
|
cert_cleanup: |
|
151
|
2
|
|
|
|
|
|
feer_tls_free_context(ctx); |
|
152
|
2
|
|
|
|
|
|
return NULL; |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
/* |
|
156
|
|
|
|
|
|
|
* Free a TLS context and its resources. |
|
157
|
|
|
|
|
|
|
*/ |
|
158
|
|
|
|
|
|
|
static void |
|
159
|
72
|
|
|
|
|
|
feer_tls_free_context(ptls_context_t *ctx) |
|
160
|
|
|
|
|
|
|
{ |
|
161
|
72
|
50
|
|
|
|
|
if (!ctx) return; |
|
162
|
72
|
50
|
|
|
|
|
if (ctx->certificates.list) { |
|
163
|
|
|
|
|
|
|
size_t i; |
|
164
|
141
|
100
|
|
|
|
|
for (i = 0; i < ctx->certificates.count; i++) |
|
165
|
69
|
|
|
|
|
|
free(ctx->certificates.list[i].base); |
|
166
|
72
|
|
|
|
|
|
free(ctx->certificates.list); |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
/* Free per-context sign certificate (allocated in feer_tls_create_context) */ |
|
169
|
72
|
100
|
|
|
|
|
if (ctx->sign_certificate) { |
|
170
|
67
|
|
|
|
|
|
ptls_openssl_sign_certificate_t *sign_cert = |
|
171
|
|
|
|
|
|
|
(ptls_openssl_sign_certificate_t *)ctx->sign_certificate; |
|
172
|
67
|
|
|
|
|
|
ptls_openssl_dispose_sign_certificate(sign_cert); |
|
173
|
67
|
|
|
|
|
|
Safefree(sign_cert); |
|
174
|
|
|
|
|
|
|
} |
|
175
|
72
|
|
|
|
|
|
Safefree(ctx); |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
/* |
|
179
|
|
|
|
|
|
|
* Reference-counted TLS context wrapper. |
|
180
|
|
|
|
|
|
|
* Prevents use-after-free when set_tls rotates certs or accept_on_fd |
|
181
|
|
|
|
|
|
|
* reuses a listener slot while active connections still hold ptls_t |
|
182
|
|
|
|
|
|
|
* objects that reference the old context. |
|
183
|
|
|
|
|
|
|
*/ |
|
184
|
|
|
|
|
|
|
static struct feer_tls_ctx_ref * |
|
185
|
68
|
|
|
|
|
|
feer_tls_ctx_ref_new(ptls_context_t *ctx) |
|
186
|
|
|
|
|
|
|
{ |
|
187
|
|
|
|
|
|
|
struct feer_tls_ctx_ref *ref; |
|
188
|
68
|
|
|
|
|
|
Newx(ref, 1, struct feer_tls_ctx_ref); |
|
189
|
68
|
|
|
|
|
|
ref->ctx = ctx; |
|
190
|
68
|
|
|
|
|
|
ref->refcount = 1; |
|
191
|
68
|
|
|
|
|
|
return ref; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
195
|
59
|
|
|
|
|
|
feer_tls_ctx_ref_inc(struct feer_tls_ctx_ref *ref) |
|
196
|
|
|
|
|
|
|
{ |
|
197
|
59
|
|
|
|
|
|
ref->refcount++; |
|
198
|
59
|
|
|
|
|
|
} |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
static void |
|
201
|
122
|
|
|
|
|
|
feer_tls_ctx_ref_dec(struct feer_tls_ctx_ref *ref) |
|
202
|
|
|
|
|
|
|
{ |
|
203
|
122
|
100
|
|
|
|
|
if (--ref->refcount <= 0) { |
|
204
|
67
|
|
|
|
|
|
feer_tls_free_context(ref->ctx); |
|
205
|
67
|
|
|
|
|
|
Safefree(ref); |
|
206
|
|
|
|
|
|
|
} |
|
207
|
122
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
/* |
|
210
|
|
|
|
|
|
|
* Initialize TLS state on a newly accepted connection. |
|
211
|
|
|
|
|
|
|
*/ |
|
212
|
|
|
|
|
|
|
static void |
|
213
|
59
|
|
|
|
|
|
feer_tls_init_conn(struct feer_conn *c, struct feer_tls_ctx_ref *ref) |
|
214
|
|
|
|
|
|
|
{ |
|
215
|
59
|
|
|
|
|
|
c->tls = ptls_new(ref->ctx, 1 /* is_server */); |
|
216
|
59
|
50
|
|
|
|
|
if (unlikely(!c->tls)) { |
|
217
|
0
|
|
|
|
|
|
trouble("ptls_new failed for fd=%d\n", c->fd); |
|
218
|
0
|
|
|
|
|
|
return; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
59
|
|
|
|
|
|
feer_tls_ctx_ref_inc(ref); |
|
221
|
59
|
|
|
|
|
|
c->tls_ctx_ref = ref; |
|
222
|
59
|
|
|
|
|
|
ptls_buffer_init(&c->tls_wbuf, "", 0); |
|
223
|
59
|
|
|
|
|
|
c->tls_tunnel_sv0 = -1; |
|
224
|
59
|
|
|
|
|
|
c->tls_tunnel_sv1 = -1; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
/* |
|
228
|
|
|
|
|
|
|
* Free TLS state on connection destruction. |
|
229
|
|
|
|
|
|
|
*/ |
|
230
|
|
|
|
|
|
|
static void |
|
231
|
580
|
|
|
|
|
|
feer_tls_free_conn(struct feer_conn *c) |
|
232
|
|
|
|
|
|
|
{ |
|
233
|
580
|
100
|
|
|
|
|
if (c->tls) { |
|
234
|
54
|
|
|
|
|
|
ptls_free(c->tls); |
|
235
|
54
|
|
|
|
|
|
c->tls = NULL; |
|
236
|
|
|
|
|
|
|
} |
|
237
|
580
|
100
|
|
|
|
|
if (c->tls_ctx_ref) { |
|
238
|
54
|
|
|
|
|
|
feer_tls_ctx_ref_dec(c->tls_ctx_ref); |
|
239
|
54
|
|
|
|
|
|
c->tls_ctx_ref = NULL; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
580
|
|
|
|
|
|
ptls_buffer_dispose(&c->tls_wbuf); |
|
242
|
580
|
50
|
|
|
|
|
if (c->tls_rbuf) { |
|
243
|
0
|
|
|
|
|
|
Safefree(c->tls_rbuf); |
|
244
|
0
|
|
|
|
|
|
c->tls_rbuf = NULL; |
|
245
|
0
|
|
|
|
|
|
c->tls_rbuf_len = 0; |
|
246
|
|
|
|
|
|
|
} |
|
247
|
580
|
100
|
|
|
|
|
if (c->tls_tunnel) { |
|
248
|
3
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_read_w); |
|
249
|
3
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_write_w); |
|
250
|
3
|
|
|
|
|
|
c->tls_tunnel = 0; |
|
251
|
3
|
50
|
|
|
|
|
if (c->tls_tunnel_sv0 >= 0) { |
|
252
|
3
|
|
|
|
|
|
close(c->tls_tunnel_sv0); |
|
253
|
3
|
|
|
|
|
|
c->tls_tunnel_sv0 = -1; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
3
|
50
|
|
|
|
|
if (c->tls_tunnel_sv1 >= 0) { |
|
256
|
0
|
|
|
|
|
|
close(c->tls_tunnel_sv1); |
|
257
|
0
|
|
|
|
|
|
c->tls_tunnel_sv1 = -1; |
|
258
|
|
|
|
|
|
|
} |
|
259
|
3
|
50
|
|
|
|
|
if (c->tls_tunnel_wbuf) { |
|
260
|
|
|
|
|
|
|
dTHX; |
|
261
|
0
|
|
|
|
|
|
SvREFCNT_dec(c->tls_tunnel_wbuf); |
|
262
|
0
|
|
|
|
|
|
c->tls_tunnel_wbuf = NULL; |
|
263
|
0
|
|
|
|
|
|
c->tls_tunnel_wbuf_pos = 0; |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
} |
|
266
|
580
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
/* |
|
269
|
|
|
|
|
|
|
* Flush accumulated encrypted data from tls_wbuf to the socket. |
|
270
|
|
|
|
|
|
|
* Returns: number of bytes written, 0 if nothing to write, -1 on EAGAIN, -2 on error. |
|
271
|
|
|
|
|
|
|
*/ |
|
272
|
|
|
|
|
|
|
static int |
|
273
|
114
|
|
|
|
|
|
feer_tls_flush_wbuf(struct feer_conn *c) |
|
274
|
|
|
|
|
|
|
{ |
|
275
|
114
|
50
|
|
|
|
|
if (c->tls_wbuf.off == 0) |
|
276
|
0
|
|
|
|
|
|
return 0; |
|
277
|
|
|
|
|
|
|
|
|
278
|
114
|
|
|
|
|
|
ssize_t written = write(c->fd, c->tls_wbuf.base, c->tls_wbuf.off); |
|
279
|
114
|
50
|
|
|
|
|
if (written < 0) { |
|
280
|
0
|
0
|
|
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
281
|
0
|
|
|
|
|
|
return -1; |
|
282
|
|
|
|
|
|
|
} |
|
283
|
|
|
|
|
|
|
trace("TLS flush write error fd=%d: %s\n", c->fd, strerror(errno)); |
|
284
|
0
|
|
|
|
|
|
return -2; |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
114
|
50
|
|
|
|
|
if ((size_t)written < c->tls_wbuf.off) { |
|
288
|
|
|
|
|
|
|
/* Partial write: shift remaining data to front */ |
|
289
|
0
|
|
|
|
|
|
memmove(c->tls_wbuf.base, c->tls_wbuf.base + written, |
|
290
|
0
|
|
|
|
|
|
c->tls_wbuf.off - written); |
|
291
|
0
|
|
|
|
|
|
c->tls_wbuf.off -= written; |
|
292
|
0
|
|
|
|
|
|
return (int)written; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
/* Full write */ |
|
296
|
114
|
|
|
|
|
|
c->tls_wbuf.off = 0; |
|
297
|
114
|
|
|
|
|
|
return (int)written; |
|
298
|
|
|
|
|
|
|
} |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
#define tls_wbuf_append(buf, src, len) ptls_buffer__do_pushv(buf, src, len) |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
/* |
|
303
|
|
|
|
|
|
|
* ======= TLS Tunnel (socketpair relay for io()/psgix.io over TLS) ======= |
|
304
|
|
|
|
|
|
|
* |
|
305
|
|
|
|
|
|
|
* When io() is called on a TLS connection, we create a socketpair: |
|
306
|
|
|
|
|
|
|
* sv[0] = Feersum's end (with ev_io watchers) |
|
307
|
|
|
|
|
|
|
* sv[1] = handler's end (returned as IO handle) |
|
308
|
|
|
|
|
|
|
* |
|
309
|
|
|
|
|
|
|
* Data flow: |
|
310
|
|
|
|
|
|
|
* App writes to sv[1] -> sv[0] readable -> encrypt via ptls -> send to TLS fd |
|
311
|
|
|
|
|
|
|
* TLS fd readable -> decrypt via ptls -> write to sv[0] -> sv[1] readable for app |
|
312
|
|
|
|
|
|
|
*/ |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
/* |
|
316
|
|
|
|
|
|
|
* Try to write data to sv[0]; buffer any remainder in tls_tunnel_wbuf. |
|
317
|
|
|
|
|
|
|
* Returns 0 on success, -1 on hard write error. |
|
318
|
|
|
|
|
|
|
*/ |
|
319
|
|
|
|
|
|
|
static int |
|
320
|
6
|
|
|
|
|
|
tls_tunnel_write_or_buffer(struct feer_conn *c, const char *data, size_t len) |
|
321
|
|
|
|
|
|
|
{ |
|
322
|
|
|
|
|
|
|
dTHX; |
|
323
|
6
|
50
|
|
|
|
|
if (c->tls_tunnel_sv0 < 0) return -1; |
|
324
|
|
|
|
|
|
|
|
|
325
|
6
|
|
|
|
|
|
ssize_t nw = write(c->tls_tunnel_sv0, data, len); |
|
326
|
6
|
50
|
|
|
|
|
if (nw == (ssize_t)len) |
|
327
|
6
|
|
|
|
|
|
return 0; |
|
328
|
|
|
|
|
|
|
|
|
329
|
0
|
0
|
|
|
|
|
if (nw < 0) { |
|
330
|
0
|
0
|
|
|
|
|
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
331
|
0
|
|
|
|
|
|
return -1; |
|
332
|
0
|
|
|
|
|
|
nw = 0; |
|
333
|
|
|
|
|
|
|
} |
|
334
|
|
|
|
|
|
|
|
|
335
|
0
|
|
|
|
|
|
size_t remaining = len - nw; |
|
336
|
0
|
0
|
|
|
|
|
if (!c->tls_tunnel_wbuf) { |
|
337
|
0
|
|
|
|
|
|
c->tls_tunnel_wbuf = newSV_buf(remaining + 256); |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
/* Compact consumed prefix to prevent unbounded SV growth */ |
|
341
|
0
|
0
|
|
|
|
|
if (c->tls_tunnel_wbuf_pos > 0) { |
|
342
|
0
|
|
|
|
|
|
STRLEN remain = SvCUR(c->tls_tunnel_wbuf) - c->tls_tunnel_wbuf_pos; |
|
343
|
0
|
0
|
|
|
|
|
if (remain > 0) |
|
344
|
0
|
|
|
|
|
|
memmove(SvPVX(c->tls_tunnel_wbuf), |
|
345
|
0
|
|
|
|
|
|
SvPVX(c->tls_tunnel_wbuf) + c->tls_tunnel_wbuf_pos, |
|
346
|
|
|
|
|
|
|
remain); |
|
347
|
0
|
|
|
|
|
|
SvCUR_set(c->tls_tunnel_wbuf, remain); |
|
348
|
0
|
|
|
|
|
|
c->tls_tunnel_wbuf_pos = 0; |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
|
|
351
|
0
|
0
|
|
|
|
|
if (SvCUR(c->tls_tunnel_wbuf) + remaining > FEER_TUNNEL_MAX_WBUF) { |
|
352
|
0
|
|
|
|
|
|
trouble("TLS tunnel wbuf overflow fd=%d\n", c->fd); |
|
353
|
0
|
|
|
|
|
|
return -1; |
|
354
|
|
|
|
|
|
|
} |
|
355
|
|
|
|
|
|
|
|
|
356
|
0
|
|
|
|
|
|
sv_catpvn(c->tls_tunnel_wbuf, data + nw, remaining); |
|
357
|
0
|
0
|
|
|
|
|
if (!ev_is_active(&c->tls_tunnel_write_w)) |
|
358
|
0
|
|
|
|
|
|
ev_io_start(feersum_ev_loop, &c->tls_tunnel_write_w); |
|
359
|
0
|
|
|
|
|
|
return 0; |
|
360
|
|
|
|
|
|
|
} |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
/* |
|
363
|
|
|
|
|
|
|
* ev_io callback: sv[0] is readable — app wrote data to sv[1]. |
|
364
|
|
|
|
|
|
|
* Read from sv[0], encrypt via picotls, send to TLS fd. |
|
365
|
|
|
|
|
|
|
*/ |
|
366
|
|
|
|
|
|
|
static void |
|
367
|
9
|
|
|
|
|
|
tls_tunnel_sv0_read_cb(EV_P_ struct ev_io *w, int revents) |
|
368
|
|
|
|
|
|
|
{ |
|
369
|
9
|
|
|
|
|
|
struct feer_conn *c = (struct feer_conn *)w->data; |
|
370
|
|
|
|
|
|
|
PERL_UNUSED_VAR(revents); |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
char buf[FEER_TUNNEL_BUFSZ]; |
|
373
|
9
|
|
|
|
|
|
ssize_t nread = read(c->tls_tunnel_sv0, buf, sizeof(buf)); |
|
374
|
|
|
|
|
|
|
|
|
375
|
9
|
50
|
|
|
|
|
if (nread == 0) { |
|
376
|
|
|
|
|
|
|
/* App closed sv[1] — EOF; close connection */ |
|
377
|
0
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_read_w); |
|
378
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS tunnel EOF"); |
|
379
|
0
|
|
|
|
|
|
return; |
|
380
|
|
|
|
|
|
|
} |
|
381
|
9
|
50
|
|
|
|
|
if (nread < 0) { |
|
382
|
0
|
0
|
|
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) |
|
|
|
0
|
|
|
|
|
|
|
383
|
0
|
|
|
|
|
|
return; |
|
384
|
0
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_read_w); |
|
385
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS tunnel read error"); |
|
386
|
0
|
|
|
|
|
|
return; |
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
/* Encrypt and queue for sending */ |
|
390
|
9
|
50
|
|
|
|
|
if (feer_tls_send(c, buf, nread) != 0) { |
|
391
|
0
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_read_w); |
|
392
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS tunnel encrypt error"); |
|
393
|
0
|
|
|
|
|
|
return; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
/* Flush encrypted data to socket */ |
|
397
|
9
|
|
|
|
|
|
int flush_ret = feer_tls_flush_wbuf(c); |
|
398
|
9
|
50
|
|
|
|
|
if (flush_ret == -2) { |
|
399
|
0
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_read_w); |
|
400
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS tunnel flush error"); |
|
401
|
0
|
|
|
|
|
|
return; |
|
402
|
|
|
|
|
|
|
} |
|
403
|
9
|
50
|
|
|
|
|
if (c->tls_wbuf.off > 0) |
|
404
|
0
|
|
|
|
|
|
start_write_watcher(c); |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
/* |
|
408
|
|
|
|
|
|
|
* ev_io callback: sv[0] is writable — drain tls_tunnel_wbuf |
|
409
|
|
|
|
|
|
|
* (decrypted client data -> app). |
|
410
|
|
|
|
|
|
|
*/ |
|
411
|
|
|
|
|
|
|
static void |
|
412
|
0
|
|
|
|
|
|
tls_tunnel_sv0_write_cb(EV_P_ struct ev_io *w, int revents) |
|
413
|
|
|
|
|
|
|
{ |
|
414
|
|
|
|
|
|
|
PERL_UNUSED_VAR(revents); |
|
415
|
0
|
|
|
|
|
|
struct feer_conn *c = (struct feer_conn *)w->data; |
|
416
|
|
|
|
|
|
|
|
|
417
|
0
|
0
|
|
|
|
|
if (!c->tls_tunnel_wbuf || |
|
418
|
0
|
0
|
|
|
|
|
SvCUR(c->tls_tunnel_wbuf) <= c->tls_tunnel_wbuf_pos) { |
|
419
|
0
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_write_w); |
|
420
|
0
|
0
|
|
|
|
|
if (c->tls_tunnel_wbuf) { |
|
421
|
0
|
|
|
|
|
|
SvCUR_set(c->tls_tunnel_wbuf, 0); |
|
422
|
0
|
|
|
|
|
|
c->tls_tunnel_wbuf_pos = 0; |
|
423
|
|
|
|
|
|
|
} |
|
424
|
0
|
|
|
|
|
|
return; |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
|
|
427
|
0
|
|
|
|
|
|
STRLEN avail = SvCUR(c->tls_tunnel_wbuf) - c->tls_tunnel_wbuf_pos; |
|
428
|
0
|
|
|
|
|
|
const char *ptr = SvPVX(c->tls_tunnel_wbuf) + c->tls_tunnel_wbuf_pos; |
|
429
|
0
|
|
|
|
|
|
ssize_t nw = write(c->tls_tunnel_sv0, ptr, avail); |
|
430
|
|
|
|
|
|
|
|
|
431
|
0
|
0
|
|
|
|
|
if (nw < 0) { |
|
432
|
0
|
0
|
|
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
433
|
0
|
|
|
|
|
|
return; |
|
434
|
0
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_write_w); |
|
435
|
0
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_read_w); |
|
436
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS tunnel write error"); |
|
437
|
0
|
|
|
|
|
|
return; |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
|
|
440
|
0
|
|
|
|
|
|
c->tls_tunnel_wbuf_pos += nw; |
|
441
|
0
|
0
|
|
|
|
|
if (c->tls_tunnel_wbuf_pos >= SvCUR(c->tls_tunnel_wbuf)) { |
|
442
|
0
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->tls_tunnel_write_w); |
|
443
|
0
|
|
|
|
|
|
SvCUR_set(c->tls_tunnel_wbuf, 0); |
|
444
|
0
|
|
|
|
|
|
c->tls_tunnel_wbuf_pos = 0; |
|
445
|
|
|
|
|
|
|
} |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
/* |
|
449
|
|
|
|
|
|
|
* Set up TLS tunnel socketpair for io()/psgix.io over TLS. |
|
450
|
|
|
|
|
|
|
*/ |
|
451
|
|
|
|
|
|
|
static void |
|
452
|
3
|
|
|
|
|
|
feer_tls_setup_tunnel(struct feer_conn *c) |
|
453
|
|
|
|
|
|
|
{ |
|
454
|
3
|
50
|
|
|
|
|
if (c->tls_tunnel) return; |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
int sv[2]; |
|
457
|
3
|
50
|
|
|
|
|
if (feer_socketpair_nb(sv) < 0) { |
|
458
|
0
|
|
|
|
|
|
trouble("socketpair/fcntl failed for TLS tunnel fd=%d: %s\n", |
|
459
|
|
|
|
|
|
|
c->fd, strerror(errno)); |
|
460
|
0
|
|
|
|
|
|
return; |
|
461
|
|
|
|
|
|
|
} |
|
462
|
|
|
|
|
|
|
|
|
463
|
3
|
|
|
|
|
|
c->tls_tunnel_sv0 = sv[0]; |
|
464
|
3
|
|
|
|
|
|
c->tls_tunnel_sv1 = sv[1]; |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
/* Read watcher: fires when app writes to sv[1] */ |
|
467
|
3
|
|
|
|
|
|
ev_io_init(&c->tls_tunnel_read_w, tls_tunnel_sv0_read_cb, sv[0], EV_READ); |
|
468
|
3
|
|
|
|
|
|
c->tls_tunnel_read_w.data = (void *)c; |
|
469
|
3
|
|
|
|
|
|
ev_io_start(feersum_ev_loop, &c->tls_tunnel_read_w); |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
/* Write watcher: initialized but NOT started until we have data to write */ |
|
472
|
3
|
|
|
|
|
|
ev_io_init(&c->tls_tunnel_write_w, tls_tunnel_sv0_write_cb, sv[0], EV_WRITE); |
|
473
|
3
|
|
|
|
|
|
c->tls_tunnel_write_w.data = (void *)c; |
|
474
|
|
|
|
|
|
|
|
|
475
|
3
|
|
|
|
|
|
c->tls_tunnel = 1; |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
/* Flush any existing rbuf data through the tunnel */ |
|
478
|
3
|
50
|
|
|
|
|
if (c->rbuf && SvOK(c->rbuf) && SvCUR(c->rbuf) > 0) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
dTHX; |
|
480
|
0
|
0
|
|
|
|
|
if (tls_tunnel_write_or_buffer(c, SvPVX(c->rbuf), SvCUR(c->rbuf)) < 0) { |
|
481
|
0
|
|
|
|
|
|
trouble("TLS tunnel rbuf flush failed fd=%d\n", c->fd); |
|
482
|
|
|
|
|
|
|
} |
|
483
|
0
|
|
|
|
|
|
SvCUR_set(c->rbuf, 0); |
|
484
|
|
|
|
|
|
|
} |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
/* Keep TLS read watcher active to receive and relay client data */ |
|
487
|
3
|
|
|
|
|
|
start_read_watcher(c); |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
trace("TLS tunnel socketpair established fd=%d sv0=%d sv1=%d\n", |
|
490
|
|
|
|
|
|
|
c->fd, sv[0], sv[1]); |
|
491
|
|
|
|
|
|
|
} |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
/* |
|
494
|
|
|
|
|
|
|
* Decrypt one TLS record from tls_rbuf into decbuf. |
|
495
|
|
|
|
|
|
|
* Returns: 0 = success (caller must ptls_buffer_dispose) |
|
496
|
|
|
|
|
|
|
* 1 = no app data (KeyUpdate/NewSessionTicket) but no error; |
|
497
|
|
|
|
|
|
|
* caller should retry if tls_rbuf still has data |
|
498
|
|
|
|
|
|
|
* -1 = error or no data available |
|
499
|
|
|
|
|
|
|
*/ |
|
500
|
|
|
|
|
|
|
static int |
|
501
|
50
|
|
|
|
|
|
feer_tls_drain_one_record(struct feer_conn *c, ptls_buffer_t *decbuf) |
|
502
|
|
|
|
|
|
|
{ |
|
503
|
50
|
50
|
|
|
|
|
if (!c->tls_rbuf || c->tls_rbuf_len == 0) return -1; |
|
|
|
0
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
|
|
505
|
0
|
|
|
|
|
|
uint8_t *saved = c->tls_rbuf; |
|
506
|
0
|
|
|
|
|
|
size_t saved_len = c->tls_rbuf_len; |
|
507
|
0
|
|
|
|
|
|
c->tls_rbuf = NULL; |
|
508
|
0
|
|
|
|
|
|
c->tls_rbuf_len = 0; |
|
509
|
|
|
|
|
|
|
|
|
510
|
0
|
|
|
|
|
|
ptls_buffer_init(decbuf, "", 0); |
|
511
|
0
|
|
|
|
|
|
size_t consumed = saved_len; |
|
512
|
0
|
|
|
|
|
|
int ret = ptls_receive(c->tls, decbuf, saved, &consumed); |
|
513
|
0
|
0
|
|
|
|
|
if (ret == 0 && consumed < saved_len) { |
|
|
|
0
|
|
|
|
|
|
|
514
|
0
|
|
|
|
|
|
size_t rem = saved_len - consumed; |
|
515
|
0
|
|
|
|
|
|
Newx(c->tls_rbuf, rem, uint8_t); |
|
516
|
0
|
|
|
|
|
|
memcpy(c->tls_rbuf, saved + consumed, rem); |
|
517
|
0
|
|
|
|
|
|
c->tls_rbuf_len = rem; |
|
518
|
|
|
|
|
|
|
} |
|
519
|
0
|
|
|
|
|
|
Safefree(saved); |
|
520
|
|
|
|
|
|
|
|
|
521
|
0
|
0
|
|
|
|
|
if (ret != 0) { |
|
522
|
0
|
|
|
|
|
|
ptls_buffer_dispose(decbuf); |
|
523
|
0
|
|
|
|
|
|
return -1; |
|
524
|
|
|
|
|
|
|
} |
|
525
|
0
|
0
|
|
|
|
|
if (decbuf->off == 0) { |
|
526
|
|
|
|
|
|
|
/* TLS 1.3 non-data record (KeyUpdate, NewSessionTicket) — no app |
|
527
|
|
|
|
|
|
|
* bytes but not an error. Caller should retry if tls_rbuf remains. */ |
|
528
|
0
|
|
|
|
|
|
ptls_buffer_dispose(decbuf); |
|
529
|
0
|
|
|
|
|
|
return 1; |
|
530
|
|
|
|
|
|
|
} |
|
531
|
0
|
|
|
|
|
|
return 0; |
|
532
|
|
|
|
|
|
|
} |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_H2 |
|
535
|
|
|
|
|
|
|
/* Drain buffered TLS records and feed them to nghttp2. |
|
536
|
|
|
|
|
|
|
* Called after initial decrypt to process any remaining records in tls_rbuf. */ |
|
537
|
|
|
|
|
|
|
static void |
|
538
|
|
|
|
|
|
|
drain_h2_tls_records(struct feer_conn *c) |
|
539
|
|
|
|
|
|
|
{ |
|
540
|
|
|
|
|
|
|
dTHX; |
|
541
|
|
|
|
|
|
|
while (c->h2_session) { |
|
542
|
|
|
|
|
|
|
ptls_buffer_t db; |
|
543
|
|
|
|
|
|
|
int drain_rv = feer_tls_drain_one_record(c, &db); |
|
544
|
|
|
|
|
|
|
if (drain_rv < 0) break; |
|
545
|
|
|
|
|
|
|
if (drain_rv == 1) continue; /* non-data TLS record */ |
|
546
|
|
|
|
|
|
|
feer_h2_session_recv(c, db.base, db.off); |
|
547
|
|
|
|
|
|
|
ptls_buffer_dispose(&db); |
|
548
|
|
|
|
|
|
|
if (c->fd < 0) break; |
|
549
|
|
|
|
|
|
|
feer_h2_session_send(c); |
|
550
|
|
|
|
|
|
|
h2_check_stream_poll_cbs(aTHX_ c); |
|
551
|
|
|
|
|
|
|
restart_read_timer(c); |
|
552
|
|
|
|
|
|
|
} |
|
553
|
|
|
|
|
|
|
} |
|
554
|
|
|
|
|
|
|
#endif |
|
555
|
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
/* |
|
557
|
|
|
|
|
|
|
* Reads encrypted data from socket, performs TLS handshake or decryption, |
|
558
|
|
|
|
|
|
|
* then feeds plaintext to either H1 parser or nghttp2. |
|
559
|
|
|
|
|
|
|
*/ |
|
560
|
|
|
|
|
|
|
static void |
|
561
|
309
|
|
|
|
|
|
try_tls_conn_read(EV_P_ ev_io *w, int revents) |
|
562
|
|
|
|
|
|
|
{ |
|
563
|
309
|
|
|
|
|
|
struct feer_conn *c = (struct feer_conn *)w->data; |
|
564
|
|
|
|
|
|
|
PERL_UNUSED_VAR(revents); |
|
565
|
|
|
|
|
|
|
PERL_UNUSED_VAR(loop); |
|
566
|
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
dTHX; |
|
568
|
309
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); /* prevent premature free during callback */ |
|
569
|
309
|
|
|
|
|
|
feer_conn_set_busy(c); |
|
570
|
|
|
|
|
|
|
trace("tls_conn_read fd=%d hs_done=%d\n", c->fd, c->tls_handshake_done); |
|
571
|
|
|
|
|
|
|
|
|
572
|
309
|
|
|
|
|
|
ssize_t got_n = 0; |
|
573
|
|
|
|
|
|
|
|
|
574
|
309
|
100
|
|
|
|
|
if (unlikely(c->pipelined)) goto tls_pipelined; |
|
575
|
|
|
|
|
|
|
|
|
576
|
308
|
50
|
|
|
|
|
if (unlikely(!c->tls)) { |
|
577
|
0
|
|
|
|
|
|
trouble("tls_conn_read: no TLS context fd=%d\n", c->fd); |
|
578
|
0
|
|
|
|
|
|
change_receiving_state(c, RECEIVE_SHUTDOWN); |
|
579
|
0
|
|
|
|
|
|
stop_all_watchers(c); |
|
580
|
0
|
|
|
|
|
|
safe_close_conn(c, "no TLS context"); |
|
581
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
582
|
|
|
|
|
|
|
} |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
uint8_t rawbuf[TLS_RAW_BUFSZ]; |
|
585
|
308
|
|
|
|
|
|
ssize_t nread = read(c->fd, rawbuf, sizeof(rawbuf)); |
|
586
|
|
|
|
|
|
|
|
|
587
|
308
|
100
|
|
|
|
|
if (nread == 0) { |
|
588
|
|
|
|
|
|
|
/* EOF */ |
|
589
|
|
|
|
|
|
|
trace("TLS EOF fd=%d\n", c->fd); |
|
590
|
3
|
|
|
|
|
|
change_receiving_state(c, RECEIVE_SHUTDOWN); |
|
591
|
3
|
|
|
|
|
|
stop_all_watchers(c); |
|
592
|
3
|
|
|
|
|
|
safe_close_conn(c, "TLS EOF"); |
|
593
|
3
|
|
|
|
|
|
goto tls_read_cleanup; |
|
594
|
|
|
|
|
|
|
} |
|
595
|
|
|
|
|
|
|
|
|
596
|
305
|
50
|
|
|
|
|
if (nread < 0) { |
|
597
|
0
|
0
|
|
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) |
|
|
|
0
|
|
|
|
|
|
|
598
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
599
|
|
|
|
|
|
|
trace("TLS read error fd=%d: %s\n", c->fd, strerror(errno)); |
|
600
|
0
|
|
|
|
|
|
change_receiving_state(c, RECEIVE_SHUTDOWN); |
|
601
|
0
|
|
|
|
|
|
stop_all_watchers(c); |
|
602
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS read error"); |
|
603
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
604
|
|
|
|
|
|
|
} |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
/* PROXY protocol: raw PROXY header arrives before the TLS handshake. |
|
607
|
|
|
|
|
|
|
* Buffer bytes into c->rbuf, parse the PROXY header, then either: |
|
608
|
|
|
|
|
|
|
* - fall through to TLS handshake if leftover bytes are present, or |
|
609
|
|
|
|
|
|
|
* - wait for next read event if the PROXY header consumed all data. */ |
|
610
|
305
|
100
|
|
|
|
|
if (unlikely(c->receiving == RECEIVE_PROXY_HEADER)) { |
|
611
|
152
|
100
|
|
|
|
|
if (!c->rbuf) { |
|
612
|
23
|
|
|
|
|
|
c->rbuf = newSV_buf(nread + 256); |
|
613
|
|
|
|
|
|
|
} |
|
614
|
152
|
|
|
|
|
|
sv_catpvn(c->rbuf, (const char *)rawbuf, nread); |
|
615
|
|
|
|
|
|
|
|
|
616
|
152
|
|
|
|
|
|
int ret = try_parse_proxy_header(c); |
|
617
|
152
|
100
|
|
|
|
|
if (ret == -1) { |
|
618
|
4
|
|
|
|
|
|
stop_all_watchers(c); |
|
619
|
4
|
|
|
|
|
|
safe_close_conn(c, "Invalid PROXY header on TLS listener"); |
|
620
|
4
|
|
|
|
|
|
goto tls_read_cleanup; |
|
621
|
|
|
|
|
|
|
} |
|
622
|
148
|
100
|
|
|
|
|
if (ret == -2) goto tls_read_cleanup; /* need more data */ |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
/* PROXY header parsed successfully — consume parsed bytes */ |
|
625
|
19
|
|
|
|
|
|
STRLEN remaining = SvCUR(c->rbuf) - ret; |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
/* Clear cached remote addr/port so they regenerate from new sockaddr */ |
|
628
|
19
|
50
|
|
|
|
|
feer_clear_remote_cache(c); |
|
|
|
50
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
|
|
630
|
19
|
|
|
|
|
|
change_receiving_state(c, RECEIVE_HEADERS); |
|
631
|
|
|
|
|
|
|
|
|
632
|
19
|
50
|
|
|
|
|
if (remaining > 0) { |
|
633
|
|
|
|
|
|
|
/* Leftover bytes are the start of the TLS ClientHello. |
|
634
|
|
|
|
|
|
|
* Save in tls_rbuf (heap) to avoid overflowing stack rawbuf |
|
635
|
|
|
|
|
|
|
* when the PROXY header spans multiple reads. */ |
|
636
|
0
|
|
|
|
|
|
Newx(c->tls_rbuf, remaining, uint8_t); |
|
637
|
0
|
|
|
|
|
|
memcpy(c->tls_rbuf, SvPVX(c->rbuf) + ret, remaining); |
|
638
|
0
|
|
|
|
|
|
c->tls_rbuf_len = remaining; |
|
639
|
0
|
|
|
|
|
|
nread = 0; |
|
640
|
|
|
|
|
|
|
} |
|
641
|
19
|
|
|
|
|
|
SvREFCNT_dec(c->rbuf); |
|
642
|
19
|
|
|
|
|
|
c->rbuf = NULL; |
|
643
|
19
|
50
|
|
|
|
|
if (remaining == 0) |
|
644
|
19
|
|
|
|
|
|
goto tls_read_cleanup; /* wait for TLS ClientHello on next read */ |
|
645
|
|
|
|
|
|
|
/* Fall through to TLS handshake with rawbuf containing TLS data */ |
|
646
|
|
|
|
|
|
|
} |
|
647
|
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
/* Merge any saved partial TLS record bytes with new data. |
|
649
|
|
|
|
|
|
|
* ptls_receive/ptls_handshake may not consume all input when a TLS record |
|
650
|
|
|
|
|
|
|
* spans two socket reads. Unconsumed bytes are saved in tls_rbuf and |
|
651
|
|
|
|
|
|
|
* prepended to the next read here. */ |
|
652
|
153
|
|
|
|
|
|
uint8_t *inbuf = rawbuf; |
|
653
|
153
|
|
|
|
|
|
size_t inlen = (size_t)nread; |
|
654
|
153
|
|
|
|
|
|
uint8_t *merged = NULL; |
|
655
|
|
|
|
|
|
|
|
|
656
|
153
|
50
|
|
|
|
|
if (c->tls_rbuf_len > 0) { |
|
657
|
0
|
0
|
|
|
|
|
if (nread > 0) { |
|
658
|
0
|
|
|
|
|
|
inlen = c->tls_rbuf_len + (size_t)nread; |
|
659
|
0
|
|
|
|
|
|
Newx(merged, inlen, uint8_t); |
|
660
|
0
|
|
|
|
|
|
memcpy(merged, c->tls_rbuf, c->tls_rbuf_len); |
|
661
|
0
|
|
|
|
|
|
memcpy(merged + c->tls_rbuf_len, rawbuf, (size_t)nread); |
|
662
|
0
|
|
|
|
|
|
inbuf = merged; |
|
663
|
0
|
|
|
|
|
|
Safefree(c->tls_rbuf); |
|
664
|
|
|
|
|
|
|
} else { |
|
665
|
|
|
|
|
|
|
/* PROXY leftover only — transfer ownership to merged, no copy */ |
|
666
|
0
|
|
|
|
|
|
merged = c->tls_rbuf; |
|
667
|
0
|
|
|
|
|
|
inbuf = merged; |
|
668
|
0
|
|
|
|
|
|
inlen = c->tls_rbuf_len; |
|
669
|
|
|
|
|
|
|
} |
|
670
|
0
|
|
|
|
|
|
c->tls_rbuf = NULL; |
|
671
|
0
|
|
|
|
|
|
c->tls_rbuf_len = 0; |
|
672
|
|
|
|
|
|
|
} |
|
673
|
|
|
|
|
|
|
|
|
674
|
153
|
100
|
|
|
|
|
if (!c->tls_handshake_done) { |
|
675
|
|
|
|
|
|
|
/* TLS handshake in progress */ |
|
676
|
55
|
|
|
|
|
|
size_t consumed = inlen; |
|
677
|
|
|
|
|
|
|
ptls_buffer_t hsbuf; |
|
678
|
55
|
|
|
|
|
|
ptls_buffer_init(&hsbuf, "", 0); |
|
679
|
|
|
|
|
|
|
|
|
680
|
55
|
|
|
|
|
|
int ret = ptls_handshake(c->tls, &hsbuf, inbuf, &consumed, NULL); |
|
681
|
|
|
|
|
|
|
|
|
682
|
55
|
100
|
|
|
|
|
if (hsbuf.off > 0) { |
|
683
|
46
|
50
|
|
|
|
|
if (tls_wbuf_append(&c->tls_wbuf, hsbuf.base, hsbuf.off) != 0) { |
|
684
|
0
|
|
|
|
|
|
trouble("TLS wbuf alloc failed during handshake fd=%d\n", c->fd); |
|
685
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&hsbuf); |
|
686
|
0
|
0
|
|
|
|
|
if (merged) Safefree(merged); |
|
687
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS allocation failure"); |
|
688
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
689
|
|
|
|
|
|
|
} |
|
690
|
46
|
|
|
|
|
|
int flush_ret = feer_tls_flush_wbuf(c); |
|
691
|
46
|
50
|
|
|
|
|
if (flush_ret == -2) { |
|
692
|
0
|
|
|
|
|
|
trouble("TLS flush error during handshake fd=%d\n", c->fd); |
|
693
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&hsbuf); |
|
694
|
0
|
0
|
|
|
|
|
if (merged) Safefree(merged); |
|
695
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS handshake flush error"); |
|
696
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
697
|
|
|
|
|
|
|
} |
|
698
|
46
|
50
|
|
|
|
|
if (flush_ret == -1 || c->tls_wbuf.off > 0) { |
|
|
|
50
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
/* Need to wait for write readiness (EAGAIN or partial write) */ |
|
700
|
0
|
|
|
|
|
|
start_write_watcher(c); |
|
701
|
|
|
|
|
|
|
} |
|
702
|
|
|
|
|
|
|
} |
|
703
|
55
|
|
|
|
|
|
ptls_buffer_dispose(&hsbuf); |
|
704
|
|
|
|
|
|
|
|
|
705
|
55
|
100
|
|
|
|
|
if (ret == 0) { |
|
706
|
|
|
|
|
|
|
/* Handshake complete */ |
|
707
|
42
|
|
|
|
|
|
c->tls_handshake_done = 1; |
|
708
|
|
|
|
|
|
|
trace("TLS handshake complete fd=%d\n", c->fd); |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
/* Check ALPN result */ |
|
711
|
42
|
|
|
|
|
|
const char *proto = ptls_get_negotiated_protocol(c->tls); |
|
712
|
42
|
50
|
|
|
|
|
if (proto && strlen(proto) == 2 && memcmp(proto, "h2", 2) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
713
|
0
|
|
|
|
|
|
c->tls_alpn_h2 = 1; |
|
714
|
|
|
|
|
|
|
trace("TLS ALPN: h2 negotiated fd=%d\n", c->fd); |
|
715
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_H2 |
|
716
|
|
|
|
|
|
|
feer_h2_init_session(c); |
|
717
|
|
|
|
|
|
|
#endif |
|
718
|
|
|
|
|
|
|
} else { |
|
719
|
|
|
|
|
|
|
trace("TLS ALPN: http/1.1 (or none) fd=%d\n", c->fd); |
|
720
|
|
|
|
|
|
|
} |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
/* Process any remaining data after handshake */ |
|
723
|
42
|
50
|
|
|
|
|
if (consumed < inlen) { |
|
724
|
0
|
|
|
|
|
|
size_t remaining = inlen - consumed; |
|
725
|
0
|
|
|
|
|
|
uint8_t *extra = inbuf + consumed; |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_H2 |
|
728
|
|
|
|
|
|
|
if (c->h2_session) { |
|
729
|
|
|
|
|
|
|
/* Feed to nghttp2 */ |
|
730
|
|
|
|
|
|
|
ptls_buffer_t decbuf; |
|
731
|
|
|
|
|
|
|
ptls_buffer_init(&decbuf, "", 0); |
|
732
|
|
|
|
|
|
|
size_t dec_consumed = remaining; |
|
733
|
|
|
|
|
|
|
int dec_ret = ptls_receive(c->tls, &decbuf, extra, &dec_consumed); |
|
734
|
|
|
|
|
|
|
if (dec_ret == 0 && dec_consumed < remaining) { |
|
735
|
|
|
|
|
|
|
size_t leftover = remaining - dec_consumed; |
|
736
|
|
|
|
|
|
|
Newx(c->tls_rbuf, leftover, uint8_t); |
|
737
|
|
|
|
|
|
|
memcpy(c->tls_rbuf, extra + dec_consumed, leftover); |
|
738
|
|
|
|
|
|
|
c->tls_rbuf_len = leftover; |
|
739
|
|
|
|
|
|
|
} |
|
740
|
|
|
|
|
|
|
if (dec_ret == 0 && decbuf.off > 0) { |
|
741
|
|
|
|
|
|
|
feer_h2_session_recv(c, decbuf.base, decbuf.off); |
|
742
|
|
|
|
|
|
|
} |
|
743
|
|
|
|
|
|
|
ptls_buffer_dispose(&decbuf); |
|
744
|
|
|
|
|
|
|
if (c->fd < 0) { |
|
745
|
|
|
|
|
|
|
if (merged) Safefree(merged); |
|
746
|
|
|
|
|
|
|
goto tls_read_cleanup; |
|
747
|
|
|
|
|
|
|
} |
|
748
|
|
|
|
|
|
|
/* Send any pending nghttp2 frames (SETTINGS etc.) */ |
|
749
|
|
|
|
|
|
|
feer_h2_session_send(c); |
|
750
|
|
|
|
|
|
|
h2_check_stream_poll_cbs(aTHX_ c); |
|
751
|
|
|
|
|
|
|
restart_read_timer(c); |
|
752
|
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
drain_h2_tls_records(c); |
|
754
|
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
if (merged) Safefree(merged); |
|
756
|
|
|
|
|
|
|
goto tls_read_cleanup; |
|
757
|
|
|
|
|
|
|
} |
|
758
|
|
|
|
|
|
|
#endif |
|
759
|
|
|
|
|
|
|
/* H1: decrypt and feed to HTTP parser */ |
|
760
|
|
|
|
|
|
|
ptls_buffer_t decbuf; |
|
761
|
0
|
|
|
|
|
|
ptls_buffer_init(&decbuf, "", 0); |
|
762
|
0
|
|
|
|
|
|
size_t dec_consumed = remaining; |
|
763
|
0
|
|
|
|
|
|
int dec_ret = ptls_receive(c->tls, &decbuf, extra, &dec_consumed); |
|
764
|
0
|
0
|
|
|
|
|
if (dec_ret == 0 && dec_consumed < remaining) { |
|
|
|
0
|
|
|
|
|
|
|
765
|
0
|
|
|
|
|
|
size_t leftover = remaining - dec_consumed; |
|
766
|
0
|
|
|
|
|
|
Newx(c->tls_rbuf, leftover, uint8_t); |
|
767
|
0
|
|
|
|
|
|
memcpy(c->tls_rbuf, extra + dec_consumed, leftover); |
|
768
|
0
|
|
|
|
|
|
c->tls_rbuf_len = leftover; |
|
769
|
|
|
|
|
|
|
} |
|
770
|
0
|
0
|
|
|
|
|
if (dec_ret == 0 && decbuf.off > 0) { |
|
|
|
0
|
|
|
|
|
|
|
771
|
0
|
|
|
|
|
|
size_t decrypted_len = decbuf.off; |
|
772
|
0
|
0
|
|
|
|
|
if (!c->rbuf) { |
|
773
|
0
|
|
|
|
|
|
c->rbuf = newSV_buf(decrypted_len + READ_BUFSZ); |
|
774
|
|
|
|
|
|
|
} |
|
775
|
0
|
|
|
|
|
|
sv_catpvn(c->rbuf, (const char *)decbuf.base, decrypted_len); |
|
776
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&decbuf); |
|
777
|
|
|
|
|
|
|
|
|
778
|
0
|
|
|
|
|
|
restart_read_timer(c); |
|
779
|
0
|
|
|
|
|
|
int parse_ret = try_parse_http(c, decrypted_len); |
|
780
|
0
|
0
|
|
|
|
|
if (parse_ret == -1) { |
|
781
|
0
|
|
|
|
|
|
respond_with_server_error(c, "Malformed request\n", 400); |
|
782
|
0
|
|
|
|
|
|
finish_receiving(c); |
|
783
|
0
|
0
|
|
|
|
|
if (merged) Safefree(merged); |
|
784
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
785
|
|
|
|
|
|
|
} |
|
786
|
0
|
0
|
|
|
|
|
if (parse_ret > 0) { |
|
787
|
0
|
0
|
|
|
|
|
if (!process_request_headers(c, parse_ret)) |
|
788
|
0
|
|
|
|
|
|
finish_receiving(c); |
|
789
|
|
|
|
|
|
|
} |
|
790
|
|
|
|
|
|
|
/* parse_ret == -2: incomplete, read watcher will get more data */ |
|
791
|
|
|
|
|
|
|
} else { |
|
792
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&decbuf); |
|
793
|
|
|
|
|
|
|
} |
|
794
|
|
|
|
|
|
|
} |
|
795
|
13
|
100
|
|
|
|
|
} else if (ret == PTLS_ERROR_IN_PROGRESS) { |
|
796
|
|
|
|
|
|
|
/* Handshake still in progress, wait for more data */ |
|
797
|
|
|
|
|
|
|
trace("TLS handshake in progress fd=%d\n", c->fd); |
|
798
|
|
|
|
|
|
|
/* Save unconsumed bytes (partial TLS handshake record) */ |
|
799
|
9
|
50
|
|
|
|
|
if (consumed < inlen) { |
|
800
|
0
|
|
|
|
|
|
size_t leftover = inlen - consumed; |
|
801
|
0
|
|
|
|
|
|
Newx(c->tls_rbuf, leftover, uint8_t); |
|
802
|
0
|
|
|
|
|
|
memcpy(c->tls_rbuf, inbuf + consumed, leftover); |
|
803
|
0
|
|
|
|
|
|
c->tls_rbuf_len = leftover; |
|
804
|
|
|
|
|
|
|
} |
|
805
|
|
|
|
|
|
|
} else { |
|
806
|
|
|
|
|
|
|
/* Handshake error */ |
|
807
|
|
|
|
|
|
|
trace("TLS handshake error fd=%d ret=%d\n", c->fd, ret); |
|
808
|
4
|
|
|
|
|
|
stop_all_watchers(c); |
|
809
|
4
|
|
|
|
|
|
safe_close_conn(c, "TLS handshake error"); |
|
810
|
|
|
|
|
|
|
} |
|
811
|
55
|
50
|
|
|
|
|
if (merged) Safefree(merged); |
|
812
|
55
|
|
|
|
|
|
goto tls_read_cleanup; |
|
813
|
|
|
|
|
|
|
} |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
/* Handshake is done - decrypt application data */ |
|
816
|
|
|
|
|
|
|
{ |
|
817
|
|
|
|
|
|
|
ptls_buffer_t decbuf; |
|
818
|
98
|
|
|
|
|
|
ptls_buffer_init(&decbuf, "", 0); |
|
819
|
98
|
|
|
|
|
|
size_t consumed = inlen; |
|
820
|
98
|
|
|
|
|
|
int ret = ptls_receive(c->tls, &decbuf, inbuf, &consumed); |
|
821
|
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
/* Save unconsumed bytes for next read (partial TLS record) */ |
|
823
|
98
|
50
|
|
|
|
|
if (ret == 0 && consumed < inlen) { |
|
|
|
50
|
|
|
|
|
|
|
824
|
0
|
|
|
|
|
|
size_t remaining = inlen - consumed; |
|
825
|
0
|
|
|
|
|
|
Newx(c->tls_rbuf, remaining, uint8_t); |
|
826
|
0
|
|
|
|
|
|
memcpy(c->tls_rbuf, inbuf + consumed, remaining); |
|
827
|
0
|
|
|
|
|
|
c->tls_rbuf_len = remaining; |
|
828
|
|
|
|
|
|
|
} |
|
829
|
|
|
|
|
|
|
|
|
830
|
98
|
50
|
|
|
|
|
if (merged) Safefree(merged); |
|
831
|
|
|
|
|
|
|
|
|
832
|
98
|
50
|
|
|
|
|
if (ret != 0) { |
|
833
|
|
|
|
|
|
|
trace("TLS receive error fd=%d ret=%d\n", c->fd, ret); |
|
834
|
|
|
|
|
|
|
if (ret == PTLS_ALERT_CLOSE_NOTIFY) { |
|
835
|
|
|
|
|
|
|
trace("TLS close_notify fd=%d\n", c->fd); |
|
836
|
|
|
|
|
|
|
} |
|
837
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&decbuf); |
|
838
|
0
|
|
|
|
|
|
change_receiving_state(c, RECEIVE_SHUTDOWN); |
|
839
|
0
|
|
|
|
|
|
stop_all_watchers(c); |
|
840
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS receive error"); |
|
841
|
48
|
|
|
|
|
|
goto tls_read_cleanup; |
|
842
|
|
|
|
|
|
|
} |
|
843
|
|
|
|
|
|
|
|
|
844
|
98
|
100
|
|
|
|
|
if (decbuf.off == 0) { |
|
845
|
42
|
|
|
|
|
|
ptls_buffer_dispose(&decbuf); |
|
846
|
42
|
|
|
|
|
|
goto tls_read_cleanup; /* No application data yet */ |
|
847
|
|
|
|
|
|
|
} |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_H2 |
|
850
|
|
|
|
|
|
|
if (c->h2_session) { |
|
851
|
|
|
|
|
|
|
/* Feed decrypted data to nghttp2 */ |
|
852
|
|
|
|
|
|
|
feer_h2_session_recv(c, decbuf.base, decbuf.off); |
|
853
|
|
|
|
|
|
|
ptls_buffer_dispose(&decbuf); |
|
854
|
|
|
|
|
|
|
if (c->fd < 0) goto tls_read_cleanup; |
|
855
|
|
|
|
|
|
|
/* Send any pending nghttp2 frames */ |
|
856
|
|
|
|
|
|
|
feer_h2_session_send(c); |
|
857
|
|
|
|
|
|
|
h2_check_stream_poll_cbs(aTHX_ c); |
|
858
|
|
|
|
|
|
|
restart_read_timer(c); |
|
859
|
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
drain_h2_tls_records(c); |
|
861
|
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
goto tls_read_cleanup; |
|
863
|
|
|
|
|
|
|
} |
|
864
|
|
|
|
|
|
|
#endif |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
/* TLS tunnel: relay decrypted data to sv[0] for the app */ |
|
867
|
56
|
100
|
|
|
|
|
if (c->tls_tunnel) { |
|
868
|
6
|
50
|
|
|
|
|
if (tls_tunnel_write_or_buffer(c, (const char *)decbuf.base, decbuf.off) < 0) { |
|
869
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&decbuf); |
|
870
|
0
|
|
|
|
|
|
stop_all_watchers(c); |
|
871
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS tunnel write error"); |
|
872
|
0
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
873
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
874
|
|
|
|
|
|
|
} |
|
875
|
6
|
|
|
|
|
|
ptls_buffer_dispose(&decbuf); |
|
876
|
6
|
|
|
|
|
|
goto tls_read_cleanup; |
|
877
|
|
|
|
|
|
|
} |
|
878
|
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
/* HTTP/1.1 over TLS: append decrypted data to rbuf */ |
|
880
|
50
|
|
|
|
|
|
got_n = (ssize_t)decbuf.off; |
|
881
|
50
|
100
|
|
|
|
|
if (!c->rbuf) { |
|
882
|
42
|
|
|
|
|
|
c->rbuf = newSV_buf(got_n + READ_BUFSZ); |
|
883
|
|
|
|
|
|
|
} |
|
884
|
50
|
|
|
|
|
|
sv_catpvn(c->rbuf, (const char *)decbuf.base, decbuf.off); |
|
885
|
50
|
|
|
|
|
|
ptls_buffer_dispose(&decbuf); |
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
/* Drain remaining TLS records from tls_rbuf */ |
|
888
|
|
|
|
|
|
|
{ |
|
889
|
|
|
|
|
|
|
ptls_buffer_t db; |
|
890
|
|
|
|
|
|
|
int drain_rv; |
|
891
|
50
|
50
|
|
|
|
|
while ((drain_rv = feer_tls_drain_one_record(c, &db)) >= 0) { |
|
892
|
0
|
0
|
|
|
|
|
if (drain_rv == 1) continue; /* non-data TLS record */ |
|
893
|
0
|
|
|
|
|
|
got_n += (ssize_t)db.off; |
|
894
|
0
|
|
|
|
|
|
sv_catpvn(c->rbuf, (const char *)db.base, db.off); |
|
895
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&db); |
|
896
|
|
|
|
|
|
|
} |
|
897
|
|
|
|
|
|
|
} |
|
898
|
|
|
|
|
|
|
} |
|
899
|
50
|
|
|
|
|
|
goto tls_parse; |
|
900
|
|
|
|
|
|
|
|
|
901
|
1
|
|
|
|
|
|
tls_pipelined: |
|
902
|
1
|
|
|
|
|
|
got_n = c->pipelined; |
|
903
|
1
|
|
|
|
|
|
c->pipelined = 0; |
|
904
|
|
|
|
|
|
|
|
|
905
|
51
|
|
|
|
|
|
tls_parse: |
|
906
|
51
|
|
|
|
|
|
restart_read_timer(c); |
|
907
|
51
|
100
|
|
|
|
|
if (c->receiving == RECEIVE_WAIT) |
|
908
|
7
|
|
|
|
|
|
change_receiving_state(c, RECEIVE_HEADERS); |
|
909
|
|
|
|
|
|
|
|
|
910
|
51
|
100
|
|
|
|
|
if (likely(c->receiving <= RECEIVE_HEADERS)) { |
|
911
|
49
|
|
|
|
|
|
int parse_ret = try_parse_http(c, (size_t)got_n); |
|
912
|
49
|
50
|
|
|
|
|
if (parse_ret == -1) { |
|
913
|
0
|
|
|
|
|
|
respond_with_server_error(c, "Malformed request\n", 400); |
|
914
|
0
|
|
|
|
|
|
finish_receiving(c); |
|
915
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
916
|
|
|
|
|
|
|
} |
|
917
|
49
|
50
|
|
|
|
|
if (parse_ret == -2) { |
|
918
|
|
|
|
|
|
|
/* Incomplete, wait for more data (read watcher already active) */ |
|
919
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
920
|
|
|
|
|
|
|
} |
|
921
|
|
|
|
|
|
|
/* Headers complete. parse_ret = body offset */ |
|
922
|
49
|
100
|
|
|
|
|
if (!process_request_headers(c, parse_ret)) |
|
923
|
47
|
|
|
|
|
|
finish_receiving(c); |
|
924
|
|
|
|
|
|
|
} |
|
925
|
2
|
50
|
|
|
|
|
else if (likely(c->receiving == RECEIVE_BODY)) { |
|
926
|
2
|
|
|
|
|
|
c->received_cl += got_n; |
|
927
|
2
|
50
|
|
|
|
|
if (c->received_cl >= c->expected_cl) { |
|
928
|
2
|
|
|
|
|
|
sched_request_callback(c); |
|
929
|
2
|
|
|
|
|
|
finish_receiving(c); |
|
930
|
|
|
|
|
|
|
} |
|
931
|
|
|
|
|
|
|
} |
|
932
|
0
|
0
|
|
|
|
|
else if (c->receiving == RECEIVE_CHUNKED) { |
|
933
|
0
|
|
|
|
|
|
int ret = try_parse_chunked(c); |
|
934
|
0
|
0
|
|
|
|
|
if (ret == -1) { |
|
935
|
0
|
|
|
|
|
|
respond_with_server_error(c, "Malformed chunked encoding\n", 400); |
|
936
|
0
|
|
|
|
|
|
finish_receiving(c); |
|
937
|
|
|
|
|
|
|
} |
|
938
|
0
|
0
|
|
|
|
|
else if (ret == 0) { |
|
939
|
0
|
|
|
|
|
|
sched_request_callback(c); |
|
940
|
0
|
|
|
|
|
|
finish_receiving(c); |
|
941
|
|
|
|
|
|
|
} |
|
942
|
|
|
|
|
|
|
/* ret == 1: need more data, watcher stays active */ |
|
943
|
|
|
|
|
|
|
} |
|
944
|
0
|
0
|
|
|
|
|
else if (c->receiving == RECEIVE_STREAMING) { |
|
945
|
0
|
|
|
|
|
|
c->received_cl += got_n; |
|
946
|
0
|
0
|
|
|
|
|
if (c->poll_read_cb) { |
|
947
|
0
|
|
|
|
|
|
call_poll_callback(c, 0); |
|
948
|
|
|
|
|
|
|
} |
|
949
|
0
|
0
|
|
|
|
|
if (c->receiving >= RECEIVE_SHUTDOWN) { |
|
950
|
0
|
|
|
|
|
|
finish_receiving(c); |
|
951
|
0
|
|
|
|
|
|
goto tls_read_cleanup; |
|
952
|
|
|
|
|
|
|
} |
|
953
|
0
|
0
|
|
|
|
|
if (c->expected_cl > 0 && c->received_cl >= c->expected_cl) |
|
|
|
0
|
|
|
|
|
|
|
954
|
0
|
|
|
|
|
|
finish_receiving(c); |
|
955
|
|
|
|
|
|
|
} |
|
956
|
|
|
|
|
|
|
|
|
957
|
0
|
|
|
|
|
|
tls_read_cleanup: |
|
958
|
309
|
|
|
|
|
|
SvREFCNT_dec(c->self); |
|
959
|
309
|
|
|
|
|
|
} |
|
960
|
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
/* |
|
962
|
|
|
|
|
|
|
* try_tls_conn_write - libev write callback for TLS connections. |
|
963
|
|
|
|
|
|
|
* |
|
964
|
|
|
|
|
|
|
* Encrypts pending response data via ptls and writes to socket. |
|
965
|
|
|
|
|
|
|
* Also handles sendfile-over-TLS (pread + encrypt + write). |
|
966
|
|
|
|
|
|
|
*/ |
|
967
|
|
|
|
|
|
|
static void |
|
968
|
56
|
|
|
|
|
|
try_tls_conn_write(EV_P_ ev_io *w, int revents) |
|
969
|
|
|
|
|
|
|
{ |
|
970
|
56
|
|
|
|
|
|
struct feer_conn *c = (struct feer_conn *)w->data; |
|
971
|
|
|
|
|
|
|
PERL_UNUSED_VAR(revents); |
|
972
|
|
|
|
|
|
|
PERL_UNUSED_VAR(loop); |
|
973
|
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
dTHX; |
|
975
|
56
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); /* prevent premature free during callback */ |
|
976
|
|
|
|
|
|
|
trace("tls_conn_write fd=%d\n", c->fd); |
|
977
|
|
|
|
|
|
|
|
|
978
|
56
|
50
|
|
|
|
|
if (unlikely(!c->tls)) { |
|
979
|
0
|
|
|
|
|
|
trouble("tls_conn_write: no TLS context fd=%d\n", c->fd); |
|
980
|
0
|
|
|
|
|
|
stop_write_watcher(c); |
|
981
|
0
|
|
|
|
|
|
goto tls_write_cleanup; |
|
982
|
|
|
|
|
|
|
} |
|
983
|
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
/* First, flush any pending encrypted data from TLS handshake or previous writes */ |
|
985
|
56
|
50
|
|
|
|
|
if (c->tls_wbuf.off > 0) { |
|
986
|
0
|
|
|
|
|
|
int flush_ret = feer_tls_flush_wbuf(c); |
|
987
|
0
|
0
|
|
|
|
|
if (flush_ret == -1) goto tls_write_cleanup; /* EAGAIN, keep write watcher active */ |
|
988
|
0
|
0
|
|
|
|
|
if (flush_ret == -2) goto tls_write_error; |
|
989
|
|
|
|
|
|
|
/* If there's still data after partial flush, keep trying */ |
|
990
|
0
|
0
|
|
|
|
|
if (c->tls_wbuf.off > 0) goto tls_write_cleanup; |
|
991
|
|
|
|
|
|
|
} |
|
992
|
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_H2 |
|
994
|
|
|
|
|
|
|
if (c->h2_session) { |
|
995
|
|
|
|
|
|
|
/* For H2, nghttp2 manages the write buffer. |
|
996
|
|
|
|
|
|
|
* Call session_send to generate frames, encrypt, and write. */ |
|
997
|
|
|
|
|
|
|
feer_h2_session_send(c); |
|
998
|
|
|
|
|
|
|
h2_check_stream_poll_cbs(aTHX_ c); |
|
999
|
|
|
|
|
|
|
if (c->tls_wbuf.off > 0) { |
|
1000
|
|
|
|
|
|
|
int flush_ret = feer_tls_flush_wbuf(c); |
|
1001
|
|
|
|
|
|
|
if (flush_ret == -1) goto tls_write_cleanup; |
|
1002
|
|
|
|
|
|
|
if (flush_ret == -2) goto tls_write_error; |
|
1003
|
|
|
|
|
|
|
} |
|
1004
|
|
|
|
|
|
|
if (c->tls_wbuf.off == 0) { |
|
1005
|
|
|
|
|
|
|
stop_write_watcher(c); |
|
1006
|
|
|
|
|
|
|
} |
|
1007
|
|
|
|
|
|
|
goto tls_write_cleanup; |
|
1008
|
|
|
|
|
|
|
} |
|
1009
|
|
|
|
|
|
|
#endif |
|
1010
|
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
/* HTTP/1.1 over TLS: encrypt wbuf_rinq (headers/body) first, then sendfile */ |
|
1012
|
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
/* Pre-encryption low-water check: if buffer is below threshold, let |
|
1014
|
|
|
|
|
|
|
* poll_cb refill before we start encrypting (matches H1 plain path). */ |
|
1015
|
56
|
50
|
|
|
|
|
if (c->wbuf_rinq && c->cached_wbuf_low_water > 0 |
|
|
|
100
|
|
|
|
|
|
|
1016
|
1
|
50
|
|
|
|
|
&& c->wbuf_len <= c->cached_wbuf_low_water |
|
1017
|
1
|
50
|
|
|
|
|
&& c->responding == RESPOND_STREAMING && c->poll_write_cb) { |
|
|
|
50
|
|
|
|
|
|
|
1018
|
1
|
50
|
|
|
|
|
if (c->poll_write_cb_is_io_handle) |
|
1019
|
0
|
|
|
|
|
|
pump_io_handle(c); |
|
1020
|
|
|
|
|
|
|
else |
|
1021
|
1
|
|
|
|
|
|
call_poll_callback(c, 1); |
|
1022
|
|
|
|
|
|
|
} |
|
1023
|
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
/* Encrypt data from wbuf_rinq (must come before sendfile to send headers first) */ |
|
1025
|
56
|
50
|
|
|
|
|
if (c->wbuf_rinq) { |
|
1026
|
|
|
|
|
|
|
struct iomatrix *m; |
|
1027
|
115
|
100
|
|
|
|
|
while ((m = (struct iomatrix *)rinq_shift(&c->wbuf_rinq)) != NULL) { |
|
1028
|
|
|
|
|
|
|
unsigned int i; |
|
1029
|
664
|
100
|
|
|
|
|
for (i = 0; i < m->count; i++) { |
|
1030
|
605
|
50
|
|
|
|
|
if (m->iov[i].iov_len == 0) continue; |
|
1031
|
|
|
|
|
|
|
|
|
1032
|
605
|
|
|
|
|
|
c->wbuf_len -= m->iov[i].iov_len; |
|
1033
|
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
ptls_buffer_t encbuf; |
|
1035
|
605
|
|
|
|
|
|
ptls_buffer_init(&encbuf, "", 0); |
|
1036
|
605
|
|
|
|
|
|
int ret = ptls_send(c->tls, &encbuf, |
|
1037
|
605
|
|
|
|
|
|
m->iov[i].iov_base, m->iov[i].iov_len); |
|
1038
|
605
|
50
|
|
|
|
|
if (ret != 0) { |
|
1039
|
|
|
|
|
|
|
unsigned int j; |
|
1040
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&encbuf); |
|
1041
|
0
|
|
|
|
|
|
trouble("ptls_send error fd=%d ret=%d\n", c->fd, ret); |
|
1042
|
0
|
0
|
|
|
|
|
for (j = 0; j < m->count; j++) { |
|
1043
|
0
|
0
|
|
|
|
|
if (m->sv[j]) SvREFCNT_dec(m->sv[j]); |
|
1044
|
|
|
|
|
|
|
} |
|
1045
|
0
|
0
|
|
|
|
|
IOMATRIX_FREE(m); |
|
1046
|
0
|
|
|
|
|
|
goto tls_write_error; |
|
1047
|
|
|
|
|
|
|
} |
|
1048
|
605
|
50
|
|
|
|
|
if (encbuf.off > 0) { |
|
1049
|
605
|
50
|
|
|
|
|
if (tls_wbuf_append(&c->tls_wbuf, encbuf.base, encbuf.off) != 0) { |
|
1050
|
|
|
|
|
|
|
unsigned int j; |
|
1051
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&encbuf); |
|
1052
|
0
|
|
|
|
|
|
trouble("TLS wbuf alloc failed fd=%d\n", c->fd); |
|
1053
|
0
|
0
|
|
|
|
|
for (j = 0; j < m->count; j++) { |
|
1054
|
0
|
0
|
|
|
|
|
if (m->sv[j]) SvREFCNT_dec(m->sv[j]); |
|
1055
|
|
|
|
|
|
|
} |
|
1056
|
0
|
0
|
|
|
|
|
IOMATRIX_FREE(m); |
|
1057
|
0
|
|
|
|
|
|
goto tls_write_error; |
|
1058
|
|
|
|
|
|
|
} |
|
1059
|
|
|
|
|
|
|
} |
|
1060
|
605
|
|
|
|
|
|
ptls_buffer_dispose(&encbuf); |
|
1061
|
|
|
|
|
|
|
} |
|
1062
|
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
/* Free the iomatrix SVs */ |
|
1064
|
664
|
100
|
|
|
|
|
for (i = 0; i < m->count; i++) { |
|
1065
|
605
|
100
|
|
|
|
|
if (m->sv[i]) SvREFCNT_dec(m->sv[i]); |
|
1066
|
|
|
|
|
|
|
} |
|
1067
|
59
|
50
|
|
|
|
|
IOMATRIX_FREE(m); |
|
1068
|
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
/* Low-water-mark: fire poll_cb to refill before encrypting more */ |
|
1070
|
59
|
100
|
|
|
|
|
if (c->cached_wbuf_low_water > 0 |
|
1071
|
4
|
50
|
|
|
|
|
&& c->wbuf_len <= c->cached_wbuf_low_water |
|
1072
|
4
|
100
|
|
|
|
|
&& c->responding == RESPOND_STREAMING && c->poll_write_cb) { |
|
|
|
50
|
|
|
|
|
|
|
1073
|
3
|
50
|
|
|
|
|
if (c->poll_write_cb_is_io_handle) |
|
1074
|
0
|
|
|
|
|
|
pump_io_handle(c); |
|
1075
|
|
|
|
|
|
|
else |
|
1076
|
3
|
|
|
|
|
|
call_poll_callback(c, 1); |
|
1077
|
|
|
|
|
|
|
/* poll_cb may have added more data — loop continues */ |
|
1078
|
|
|
|
|
|
|
} |
|
1079
|
|
|
|
|
|
|
} |
|
1080
|
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
/* Flush all encrypted data */ |
|
1082
|
56
|
|
|
|
|
|
int flush_ret = feer_tls_flush_wbuf(c); |
|
1083
|
56
|
50
|
|
|
|
|
if (flush_ret == -1) goto tls_write_cleanup; /* EAGAIN */ |
|
1084
|
56
|
50
|
|
|
|
|
if (flush_ret == -2) goto tls_write_error; |
|
1085
|
56
|
50
|
|
|
|
|
if (flush_ret > 0) restart_write_timer(c); |
|
1086
|
|
|
|
|
|
|
} |
|
1087
|
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
/* Handle sendfile over TLS: pread + encrypt + write */ |
|
1089
|
56
|
100
|
|
|
|
|
if (c->sendfile_fd >= 0 && c->sendfile_remain > 0) { |
|
|
|
50
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
uint8_t filebuf[TLS_RAW_BUFSZ]; |
|
1091
|
1
|
|
|
|
|
|
size_t to_read = c->sendfile_remain; |
|
1092
|
1
|
50
|
|
|
|
|
if (to_read > sizeof(filebuf)) to_read = sizeof(filebuf); |
|
1093
|
|
|
|
|
|
|
|
|
1094
|
1
|
|
|
|
|
|
ssize_t file_nread = pread(c->sendfile_fd, filebuf, to_read, c->sendfile_off); |
|
1095
|
1
|
50
|
|
|
|
|
if (file_nread <= 0) { |
|
1096
|
0
|
0
|
|
|
|
|
if (file_nread < 0) |
|
1097
|
0
|
|
|
|
|
|
trouble("TLS pread(sendfile_fd) fd=%d: %s\n", c->fd, strerror(errno)); |
|
1098
|
0
|
0
|
|
|
|
|
CLOSE_SENDFILE_FD(c); |
|
|
|
0
|
|
|
|
|
|
|
1099
|
0
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
1100
|
1
|
|
|
|
|
|
goto tls_write_finished; |
|
1101
|
|
|
|
|
|
|
} |
|
1102
|
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
/* Encrypt file data */ |
|
1104
|
|
|
|
|
|
|
ptls_buffer_t encbuf; |
|
1105
|
1
|
|
|
|
|
|
ptls_buffer_init(&encbuf, "", 0); |
|
1106
|
1
|
|
|
|
|
|
int ret = ptls_send(c->tls, &encbuf, filebuf, file_nread); |
|
1107
|
1
|
50
|
|
|
|
|
if (ret != 0) { |
|
1108
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&encbuf); |
|
1109
|
0
|
|
|
|
|
|
trouble("ptls_send(sendfile) error fd=%d ret=%d\n", c->fd, ret); |
|
1110
|
0
|
0
|
|
|
|
|
CLOSE_SENDFILE_FD(c); |
|
|
|
0
|
|
|
|
|
|
|
1111
|
0
|
|
|
|
|
|
goto tls_write_error; |
|
1112
|
|
|
|
|
|
|
} |
|
1113
|
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
/* Queue encrypted data */ |
|
1115
|
1
|
50
|
|
|
|
|
if (encbuf.off > 0) { |
|
1116
|
1
|
50
|
|
|
|
|
if (tls_wbuf_append(&c->tls_wbuf, encbuf.base, encbuf.off) != 0) { |
|
1117
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&encbuf); |
|
1118
|
0
|
|
|
|
|
|
trouble("TLS wbuf alloc failed (sendfile) fd=%d\n", c->fd); |
|
1119
|
0
|
0
|
|
|
|
|
CLOSE_SENDFILE_FD(c); |
|
|
|
0
|
|
|
|
|
|
|
1120
|
0
|
|
|
|
|
|
goto tls_write_error; |
|
1121
|
|
|
|
|
|
|
} |
|
1122
|
|
|
|
|
|
|
} |
|
1123
|
1
|
|
|
|
|
|
ptls_buffer_dispose(&encbuf); |
|
1124
|
|
|
|
|
|
|
|
|
1125
|
1
|
|
|
|
|
|
c->sendfile_off += file_nread; |
|
1126
|
1
|
|
|
|
|
|
c->sendfile_remain -= file_nread; |
|
1127
|
|
|
|
|
|
|
|
|
1128
|
1
|
50
|
|
|
|
|
if (c->sendfile_remain == 0) |
|
1129
|
1
|
50
|
|
|
|
|
CLOSE_SENDFILE_FD(c); |
|
|
|
50
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
/* Flush encrypted data */ |
|
1132
|
|
|
|
|
|
|
{ |
|
1133
|
1
|
|
|
|
|
|
int sf_flush_ret = feer_tls_flush_wbuf(c); |
|
1134
|
1
|
50
|
|
|
|
|
if (sf_flush_ret == -1) goto tls_write_cleanup; /* EAGAIN */ |
|
1135
|
1
|
50
|
|
|
|
|
if (sf_flush_ret == -2) goto tls_write_error; |
|
1136
|
|
|
|
|
|
|
} |
|
1137
|
1
|
50
|
|
|
|
|
if (c->sendfile_remain > 0 || c->tls_wbuf.off > 0) |
|
|
|
50
|
|
|
|
|
|
|
1138
|
0
|
|
|
|
|
|
goto tls_write_cleanup; /* More to send, keep watcher active */ |
|
1139
|
1
|
|
|
|
|
|
goto tls_write_finished; |
|
1140
|
|
|
|
|
|
|
} |
|
1141
|
|
|
|
|
|
|
|
|
1142
|
55
|
|
|
|
|
|
tls_write_finished: |
|
1143
|
56
|
50
|
|
|
|
|
if ((!c->wbuf_rinq || (c->cached_wbuf_low_water > 0 |
|
|
|
0
|
|
|
|
|
|
|
1144
|
0
|
0
|
|
|
|
|
&& c->wbuf_len <= c->cached_wbuf_low_water)) |
|
1145
|
56
|
50
|
|
|
|
|
&& c->sendfile_fd < 0 && c->tls_wbuf.off == 0) { |
|
|
|
50
|
|
|
|
|
|
|
1146
|
56
|
100
|
|
|
|
|
if (c->responding == RESPOND_SHUTDOWN || c->responding == RESPOND_NORMAL) { |
|
|
|
50
|
|
|
|
|
|
|
1147
|
46
|
|
|
|
|
|
handle_keepalive_or_close(c, try_tls_conn_read); |
|
1148
|
10
|
50
|
|
|
|
|
} else if (c->responding == RESPOND_STREAMING && c->poll_write_cb) { |
|
|
|
50
|
|
|
|
|
|
|
1149
|
10
|
50
|
|
|
|
|
if (c->poll_write_cb_is_io_handle) |
|
1150
|
0
|
|
|
|
|
|
pump_io_handle(c); |
|
1151
|
|
|
|
|
|
|
else |
|
1152
|
10
|
|
|
|
|
|
call_poll_callback(c, 1 /* is_write */); |
|
1153
|
0
|
0
|
|
|
|
|
} else if (c->responding == RESPOND_STREAMING) { |
|
1154
|
0
|
|
|
|
|
|
stop_write_watcher(c); |
|
1155
|
0
|
|
|
|
|
|
stop_write_timer(c); |
|
1156
|
|
|
|
|
|
|
} |
|
1157
|
|
|
|
|
|
|
} |
|
1158
|
56
|
|
|
|
|
|
goto tls_write_cleanup; |
|
1159
|
|
|
|
|
|
|
|
|
1160
|
0
|
|
|
|
|
|
tls_write_error: |
|
1161
|
0
|
|
|
|
|
|
stop_all_watchers(c); |
|
1162
|
0
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
1163
|
0
|
|
|
|
|
|
safe_close_conn(c, "TLS write error"); |
|
1164
|
|
|
|
|
|
|
|
|
1165
|
56
|
|
|
|
|
|
tls_write_cleanup: |
|
1166
|
56
|
|
|
|
|
|
SvREFCNT_dec(c->self); |
|
1167
|
56
|
|
|
|
|
|
} |
|
1168
|
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
/* |
|
1170
|
|
|
|
|
|
|
* Encrypt response data and queue for TLS writing. |
|
1171
|
|
|
|
|
|
|
* Returns 0 on success, -1 on error. |
|
1172
|
|
|
|
|
|
|
*/ |
|
1173
|
|
|
|
|
|
|
static int |
|
1174
|
11
|
|
|
|
|
|
feer_tls_send(struct feer_conn *c, const void *data, size_t len) |
|
1175
|
|
|
|
|
|
|
{ |
|
1176
|
11
|
50
|
|
|
|
|
if (!c->tls || len == 0) return 0; |
|
|
|
50
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
ptls_buffer_t encbuf; |
|
1179
|
11
|
|
|
|
|
|
ptls_buffer_init(&encbuf, "", 0); |
|
1180
|
11
|
|
|
|
|
|
int ret = ptls_send(c->tls, &encbuf, data, len); |
|
1181
|
11
|
50
|
|
|
|
|
if (ret != 0) { |
|
1182
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&encbuf); |
|
1183
|
0
|
|
|
|
|
|
trouble("feer_tls_send error fd=%d ret=%d\n", c->fd, ret); |
|
1184
|
0
|
|
|
|
|
|
return -1; |
|
1185
|
|
|
|
|
|
|
} |
|
1186
|
11
|
50
|
|
|
|
|
if (encbuf.off > 0) { |
|
1187
|
11
|
50
|
|
|
|
|
if (tls_wbuf_append(&c->tls_wbuf, encbuf.base, encbuf.off) != 0) { |
|
1188
|
0
|
|
|
|
|
|
ptls_buffer_dispose(&encbuf); |
|
1189
|
0
|
|
|
|
|
|
trouble("TLS wbuf alloc failed (send) fd=%d\n", c->fd); |
|
1190
|
0
|
|
|
|
|
|
return -1; |
|
1191
|
|
|
|
|
|
|
} |
|
1192
|
|
|
|
|
|
|
} |
|
1193
|
11
|
|
|
|
|
|
ptls_buffer_dispose(&encbuf); |
|
1194
|
11
|
|
|
|
|
|
return 0; |
|
1195
|
|
|
|
|
|
|
} |
|
1196
|
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
#endif /* FEERSUM_HAS_TLS */ |