File Coverage

feersum_tls.c.inc
Criterion Covered Total %
statement 295 571 51.6
branch 164 408 40.2
condition n/a
subroutine n/a
pod n/a
total 459 979 46.8


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 */