File Coverage

feersum_tls.c.inc
Criterion Covered Total %
statement 307 634 48.4
branch 167 458 36.4
condition n/a
subroutine n/a
pod n/a
total 474 1092 43.4


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 51           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 102 100         for (i = 0; i < num_protos; i++) {
35 51 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 51           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 51           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 51           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 87           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 87           Newxz(ctx, 1, ptls_context_t);
100              
101 87           ctx->random_bytes = ptls_openssl_random_bytes;
102 87           ctx->get_time = &ptls_get_time;
103 87           ctx->key_exchanges = feer_tls_key_exchanges;
104 87           ctx->cipher_suites = feer_tls_cipher_suites;
105              
106             /* Load certificate chain */
107 87           ret = ptls_load_certificates(ctx, cert_file);
108 87 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 84           fp = fopen(key_file, "r");
117 84 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 82           EVP_PKEY *pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
124 82           fclose(fp);
125 82 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 82           Newx(sign_cert, 1, ptls_openssl_sign_certificate_t);
132 82 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 82           EVP_PKEY_free(pkey);
139 82           ctx->sign_certificate = &sign_cert->super;
140              
141             /* ALPN negotiation via on_client_hello callback */
142 82 50         if (h2)
143 0           ctx->on_client_hello = &on_client_hello;
144             else
145 82           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 82           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 86           feer_tls_free_context(ptls_context_t *ctx)
160             {
161 86 50         if (!ctx) return;
162 86 50         if (ctx->certificates.list) {
163             size_t i;
164 169 100         for (i = 0; i < ctx->certificates.count; i++)
165 83           free(ctx->certificates.list[i].base);
166 86           free(ctx->certificates.list);
167             }
168             /* Free per-context sign certificate (allocated in feer_tls_create_context) */
169 86 100         if (ctx->sign_certificate) {
170 81           ptls_openssl_sign_certificate_t *sign_cert =
171             (ptls_openssl_sign_certificate_t *)ctx->sign_certificate;
172 81           ptls_openssl_dispose_sign_certificate(sign_cert);
173 81           Safefree(sign_cert);
174             }
175 86           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 82           feer_tls_ctx_ref_new(ptls_context_t *ctx)
186             {
187             struct feer_tls_ctx_ref *ref;
188 82           Newx(ref, 1, struct feer_tls_ctx_ref);
189 82           ref->ctx = ctx;
190 82           ref->refcount = 1;
191 82           return ref;
192             }
193              
194             INLINE_UNLESS_DEBUG static void
195 68           feer_tls_ctx_ref_inc(struct feer_tls_ctx_ref *ref)
196             {
197 68           ref->refcount++;
198 68           }
199              
200             static void
201 145           feer_tls_ctx_ref_dec(struct feer_tls_ctx_ref *ref)
202             {
203 145 100         if (--ref->refcount <= 0) {
204 81           feer_tls_free_context(ref->ctx);
205 81           Safefree(ref);
206             }
207 145           }
208              
209             /*
210             * Initialize TLS state on a newly accepted connection.
211             */
212             static void
213 68           feer_tls_init_conn(struct feer_conn *c, struct feer_tls_ctx_ref *ref)
214             {
215 68           c->tls = ptls_new(ref->ctx, 1 /* is_server */);
216 68 50         if (unlikely(!c->tls)) {
217 0           trouble("ptls_new failed for fd=%d\n", c->fd);
218 0           return;
219             }
220 68           feer_tls_ctx_ref_inc(ref);
221 68           c->tls_ctx_ref = ref;
222 68           ptls_buffer_init(&c->tls_wbuf, "", 0);
223 68           c->tls_tunnel_sv0 = -1;
224 68           c->tls_tunnel_sv1 = -1;
225             }
226              
227             /*
228             * Free TLS state on connection destruction.
229             */
230             static void
231 625           feer_tls_free_conn(struct feer_conn *c)
232             {
233 625 100         if (c->tls) {
234 63           ptls_free(c->tls);
235 63           c->tls = NULL;
236             }
237 625 100         if (c->tls_ctx_ref) {
238 63           feer_tls_ctx_ref_dec(c->tls_ctx_ref);
239 63           c->tls_ctx_ref = NULL;
240             }
241 625           ptls_buffer_dispose(&c->tls_wbuf);
242 625 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 625 100         if (c->tls_tunnel) {
248 9           ev_io_stop(feersum_ev_loop, &c->tls_tunnel_read_w);
249 9           ev_io_stop(feersum_ev_loop, &c->tls_tunnel_write_w);
250 9           c->tls_tunnel = 0;
251 9 50         if (c->tls_tunnel_sv0 >= 0) {
252 9           close(c->tls_tunnel_sv0);
253 9           c->tls_tunnel_sv0 = -1;
254             }
255 9 50         if (c->tls_tunnel_sv1 >= 0) {
256 0           close(c->tls_tunnel_sv1);
257 0           c->tls_tunnel_sv1 = -1;
258             }
259 9 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 625           }
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 140           feer_tls_flush_wbuf(struct feer_conn *c)
274             {
275 140 50         if (c->tls_wbuf.off == 0)
276 0           return 0;
277              
278 140           ssize_t written = write(c->fd, c->tls_wbuf.base, c->tls_wbuf.off);
279 140 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 140 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 140           c->tls_wbuf.off = 0;
297 140           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 13           tls_tunnel_write_or_buffer(struct feer_conn *c, const char *data, size_t len)
321             {
322             dTHX;
323 13 50         if (c->tls_tunnel_sv0 < 0) return -1;
324              
325 13           ssize_t nw = write(c->tls_tunnel_sv0, data, len);
326 13 50         if (nw == (ssize_t)len)
327 13           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 int feer_tls_drain_one_record(struct feer_conn *c, ptls_buffer_t *decbuf);
367              
368             static void
369 13           drain_tls_tunnel_rbuf(struct feer_conn *c)
370             {
371 13 50         while (c->tls_tunnel && c->tls_rbuf_len > 0) {
    50          
372 0 0         if (ev_is_active(&c->tls_tunnel_write_w))
373 0           break; /* sv[0] full — let write watcher drain first */
374             ptls_buffer_t db;
375 0           int drain_rv = feer_tls_drain_one_record(c, &db);
376 0 0         if (drain_rv < 0) break;
377 0 0         if (drain_rv == 1) continue; /* non-data (e.g. KeyUpdate) */
378 0 0         if (tls_tunnel_write_or_buffer(c, (const char *)db.base, db.off) < 0) {
379 0           ptls_buffer_dispose(&db);
380 0           stop_all_watchers(c);
381 0           safe_close_conn(c, "TLS tunnel drain error");
382 0           change_responding_state(c, RESPOND_SHUTDOWN);
383 0           return;
384             }
385 0           ptls_buffer_dispose(&db);
386             }
387             }
388              
389             static void
390 23           tls_tunnel_sv0_read_cb(EV_P_ struct ev_io *w, int revents)
391             {
392 23           struct feer_conn *c = (struct feer_conn *)w->data;
393             PERL_UNUSED_VAR(revents);
394 23           SvREFCNT_inc_void_NN(c->self);
395              
396             char buf[FEER_TUNNEL_BUFSZ];
397 23           ssize_t nread = read(c->tls_tunnel_sv0, buf, sizeof(buf));
398              
399 23 100         if (nread == 0) {
400             /* App closed sv[1] — EOF; close connection */
401 2           stop_all_watchers(c);
402 2           safe_close_conn(c, "TLS tunnel EOF");
403 2           goto cleanup;
404             }
405 21 50         if (nread < 0) {
406 0 0         if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
    0          
    0          
407 0           goto cleanup;
408 0           stop_all_watchers(c);
409 0           safe_close_conn(c, "TLS tunnel read error");
410 0           goto cleanup;
411             }
412              
413             /* Encrypt and queue for sending */
414 21 50         if (feer_tls_send(c, buf, nread) != 0) {
415 0           stop_all_watchers(c);
416 0           safe_close_conn(c, "TLS tunnel encrypt error");
417 0           goto cleanup;
418             }
419              
420             /* Flush encrypted data to socket */
421 21           int flush_ret = feer_tls_flush_wbuf(c);
422 21 50         if (flush_ret == -2) {
423 0           stop_all_watchers(c);
424 0           safe_close_conn(c, "TLS tunnel flush error");
425 0           goto cleanup;
426             }
427 21 50         if (c->tls_wbuf.off > 0)
428 0           start_write_watcher(c);
429              
430 21           cleanup:
431 23           SvREFCNT_dec(c->self);
432 23           }
433              
434             /*
435             * ev_io callback: sv[0] is writable — drain tls_tunnel_wbuf
436             * (decrypted client data -> app).
437             */
438             static void
439 0           tls_tunnel_sv0_write_cb(EV_P_ struct ev_io *w, int revents)
440             {
441             PERL_UNUSED_VAR(revents);
442 0           struct feer_conn *c = (struct feer_conn *)w->data;
443 0           SvREFCNT_inc_void_NN(c->self);
444              
445 0 0         if (!c->tls_tunnel_wbuf ||
446 0 0         SvCUR(c->tls_tunnel_wbuf) <= c->tls_tunnel_wbuf_pos) {
447 0           ev_io_stop(feersum_ev_loop, &c->tls_tunnel_write_w);
448 0 0         if (c->tls_tunnel_wbuf) {
449 0           SvCUR_set(c->tls_tunnel_wbuf, 0);
450 0           c->tls_tunnel_wbuf_pos = 0;
451             }
452 0           goto cleanup;
453             }
454              
455 0           STRLEN avail = SvCUR(c->tls_tunnel_wbuf) - c->tls_tunnel_wbuf_pos;
456 0           const char *ptr = SvPVX(c->tls_tunnel_wbuf) + c->tls_tunnel_wbuf_pos;
457 0           ssize_t nw = write(c->tls_tunnel_sv0, ptr, avail);
458              
459 0 0         if (nw < 0) {
460 0 0         if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
    0          
    0          
461 0           goto cleanup;
462 0           stop_all_watchers(c);
463 0           safe_close_conn(c, "TLS tunnel write error");
464 0           goto cleanup;
465             }
466              
467 0           c->tls_tunnel_wbuf_pos += nw;
468 0 0         if (c->tls_tunnel_wbuf_pos >= SvCUR(c->tls_tunnel_wbuf)) {
469 0           ev_io_stop(feersum_ev_loop, &c->tls_tunnel_write_w);
470 0           SvCUR_set(c->tls_tunnel_wbuf, 0);
471 0           c->tls_tunnel_wbuf_pos = 0;
472              
473             /* Resume draining tls_rbuf now that sv[0] has space */
474 0 0         if (c->tls_rbuf_len > 0)
475 0           drain_tls_tunnel_rbuf(c);
476             }
477              
478 0           cleanup:
479 0           SvREFCNT_dec(c->self);
480 0           }
481              
482             /*
483             * Set up TLS tunnel socketpair for io()/psgix.io over TLS.
484             */
485             static void
486 9           feer_tls_setup_tunnel(struct feer_conn *c)
487             {
488 9 50         if (c->tls_tunnel) return;
489              
490             int sv[2];
491 9 50         if (feer_socketpair_nb(sv) < 0) {
492 0           trouble("socketpair/fcntl failed for TLS tunnel fd=%d: %s\n",
493             c->fd, strerror(errno));
494 0           return;
495             }
496              
497 9           c->tls_tunnel_sv0 = sv[0];
498 9           c->tls_tunnel_sv1 = sv[1];
499              
500             /* Read watcher: fires when app writes to sv[1] */
501 9           ev_io_init(&c->tls_tunnel_read_w, tls_tunnel_sv0_read_cb, sv[0], EV_READ);
502 9           c->tls_tunnel_read_w.data = (void *)c;
503 9           ev_io_start(feersum_ev_loop, &c->tls_tunnel_read_w);
504              
505             /* Write watcher: initialized but NOT started until we have data to write */
506 9           ev_io_init(&c->tls_tunnel_write_w, tls_tunnel_sv0_write_cb, sv[0], EV_WRITE);
507 9           c->tls_tunnel_write_w.data = (void *)c;
508              
509 9           c->tls_tunnel = 1;
510              
511             /* Flush any existing rbuf data through the tunnel */
512 9 50         if (c->rbuf && SvOK(c->rbuf) && SvCUR(c->rbuf) > 0) {
    50          
    50          
513             dTHX;
514 0 0         if (tls_tunnel_write_or_buffer(c, SvPVX(c->rbuf), SvCUR(c->rbuf)) < 0) {
515 0           trouble("TLS tunnel rbuf flush failed fd=%d\n", c->fd);
516             }
517 0           SvCUR_set(c->rbuf, 0);
518             }
519              
520             /* Keep TLS read watcher active to receive and relay client data */
521 9           start_read_watcher(c);
522              
523             trace("TLS tunnel socketpair established fd=%d sv0=%d sv1=%d\n",
524             c->fd, sv[0], sv[1]);
525             }
526              
527             /*
528             * Decrypt one TLS record from tls_rbuf into decbuf.
529             * Returns: 0 = success (caller must ptls_buffer_dispose)
530             * 1 = no app data (KeyUpdate/NewSessionTicket) but no error;
531             * caller should retry if tls_rbuf still has data
532             * -1 = error or no data available
533             */
534             static int
535 60           feer_tls_drain_one_record(struct feer_conn *c, ptls_buffer_t *decbuf)
536             {
537 60 50         if (!c->tls_rbuf || c->tls_rbuf_len == 0) return -1;
    0          
538              
539 0           uint8_t *saved = c->tls_rbuf;
540 0           size_t saved_len = c->tls_rbuf_len;
541 0           c->tls_rbuf = NULL;
542 0           c->tls_rbuf_len = 0;
543              
544 0           ptls_buffer_init(decbuf, "", 0);
545 0           size_t consumed = saved_len;
546 0           int ret = ptls_receive(c->tls, decbuf, saved, &consumed);
547 0 0         if (ret == 0 && consumed < saved_len) {
    0          
548 0           size_t rem = saved_len - consumed;
549 0           Newx(c->tls_rbuf, rem, uint8_t);
550 0           memcpy(c->tls_rbuf, saved + consumed, rem);
551 0           c->tls_rbuf_len = rem;
552             }
553 0           Safefree(saved);
554              
555 0 0         if (ret != 0) {
556 0           ptls_buffer_dispose(decbuf);
557 0           return -1;
558             }
559 0 0         if (decbuf->off == 0) {
560             /* TLS 1.3 non-data record (KeyUpdate, NewSessionTicket) — no app
561             * bytes but not an error. Caller should retry if tls_rbuf remains. */
562 0           ptls_buffer_dispose(decbuf);
563 0           return 1;
564             }
565 0           return 0;
566             }
567              
568             #ifdef FEERSUM_HAS_H2
569             /* Drain buffered TLS records and feed them to nghttp2.
570             * Called after initial decrypt to process any remaining records in tls_rbuf. */
571             static void
572             drain_h2_tls_records(struct feer_conn *c)
573             {
574             dTHX;
575             while (c->h2_session) {
576             ptls_buffer_t db;
577             int drain_rv = feer_tls_drain_one_record(c, &db);
578             if (drain_rv < 0) break;
579             if (drain_rv == 1) continue; /* non-data TLS record */
580             feer_h2_session_recv(c, db.base, db.off);
581             ptls_buffer_dispose(&db);
582             if (c->fd < 0) break;
583             feer_h2_session_send(c);
584             h2_check_stream_poll_cbs(aTHX_ c);
585             restart_read_timer(c);
586             }
587             }
588             #endif
589              
590             /*
591             * Reads encrypted data from socket, performs TLS handshake or decryption,
592             * then feeds plaintext to either H1 parser or nghttp2.
593             */
594             static void
595 348           try_tls_conn_read(EV_P_ ev_io *w, int revents)
596             {
597 348           struct feer_conn *c = (struct feer_conn *)w->data;
598             PERL_UNUSED_VAR(revents);
599             PERL_UNUSED_VAR(loop);
600              
601             dTHX;
602 348           SvREFCNT_inc_void_NN(c->self); /* prevent premature free during callback */
603 348           feer_conn_set_busy(c);
604             trace("tls_conn_read fd=%d hs_done=%d\n", c->fd, c->tls_handshake_done);
605              
606 348           ssize_t got_n = 0;
607              
608 348 100         if (unlikely(c->pipelined)) goto tls_pipelined;
609              
610 347 50         if (unlikely(!c->tls)) {
611 0           trouble("tls_conn_read: no TLS context fd=%d\n", c->fd);
612 0           change_receiving_state(c, RECEIVE_SHUTDOWN);
613 0           stop_all_watchers(c);
614 0           safe_close_conn(c, "no TLS context");
615 0           goto tls_read_cleanup;
616             }
617              
618             uint8_t rawbuf[TLS_RAW_BUFSZ];
619 347           ssize_t nread = read(c->fd, rawbuf, sizeof(rawbuf));
620              
621 347 100         if (nread == 0) {
622             /* EOF */
623             trace("TLS EOF fd=%d\n", c->fd);
624 7           change_receiving_state(c, RECEIVE_SHUTDOWN);
625 7           stop_all_watchers(c);
626 7           safe_close_conn(c, "TLS EOF");
627 7           goto tls_read_cleanup;
628             }
629              
630 340 50         if (nread < 0) {
631 0 0         if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
    0          
    0          
632 0           goto tls_read_cleanup;
633             trace("TLS read error fd=%d: %s\n", c->fd, strerror(errno));
634 0           change_receiving_state(c, RECEIVE_SHUTDOWN);
635 0           stop_all_watchers(c);
636 0           safe_close_conn(c, "TLS read error");
637 0           goto tls_read_cleanup;
638             }
639              
640             /* PROXY protocol: raw PROXY header arrives before the TLS handshake.
641             * Buffer bytes into c->rbuf, parse the PROXY header, then either:
642             * - fall through to TLS handshake if leftover bytes are present, or
643             * - wait for next read event if the PROXY header consumed all data. */
644 340 100         if (unlikely(c->receiving == RECEIVE_PROXY_HEADER)) {
645 152 100         if (!c->rbuf) {
646 23           c->rbuf = newSV_buf(nread + 256);
647             }
648 152           sv_catpvn(c->rbuf, (const char *)rawbuf, nread);
649              
650 152           int ret = try_parse_proxy_header(c);
651 152 100         if (ret == -1) {
652 4           stop_all_watchers(c);
653 4           safe_close_conn(c, "Invalid PROXY header on TLS listener");
654 4           goto tls_read_cleanup;
655             }
656 148 100         if (ret == -2) goto tls_read_cleanup; /* need more data */
657              
658             /* PROXY header parsed successfully — consume parsed bytes */
659 19           STRLEN remaining = SvCUR(c->rbuf) - ret;
660              
661             /* Clear cached remote addr/port so they regenerate from new sockaddr */
662 19 50         feer_clear_remote_cache(c);
    50          
663              
664 19           change_receiving_state(c, RECEIVE_HEADERS);
665              
666 19 50         if (remaining > 0) {
667             /* Leftover bytes are the start of the TLS ClientHello.
668             * Save in tls_rbuf (heap) to avoid overflowing stack rawbuf
669             * when the PROXY header spans multiple reads. */
670 0           Newx(c->tls_rbuf, remaining, uint8_t);
671 0           memcpy(c->tls_rbuf, SvPVX(c->rbuf) + ret, remaining);
672 0           c->tls_rbuf_len = remaining;
673 0           nread = 0;
674             }
675 19           SvREFCNT_dec(c->rbuf);
676 19           c->rbuf = NULL;
677 19 50         if (remaining == 0)
678 19           goto tls_read_cleanup; /* wait for TLS ClientHello on next read */
679             /* Fall through to TLS handshake with rawbuf containing TLS data */
680             }
681              
682             /* Merge any saved partial TLS record bytes with new data.
683             * ptls_receive/ptls_handshake may not consume all input when a TLS record
684             * spans two socket reads. Unconsumed bytes are saved in tls_rbuf and
685             * prepended to the next read here. */
686 188           uint8_t *inbuf = rawbuf;
687 188           size_t inlen = (size_t)nread;
688 188           uint8_t *merged = NULL;
689              
690 188 50         if (c->tls_rbuf_len > 0) {
691 0 0         if (nread > 0) {
692 0           inlen = c->tls_rbuf_len + (size_t)nread;
693 0           Newx(merged, inlen, uint8_t);
694 0           memcpy(merged, c->tls_rbuf, c->tls_rbuf_len);
695 0           memcpy(merged + c->tls_rbuf_len, rawbuf, (size_t)nread);
696 0           inbuf = merged;
697 0           Safefree(c->tls_rbuf);
698             } else {
699             /* PROXY leftover only — transfer ownership to merged, no copy */
700 0           merged = c->tls_rbuf;
701 0           inbuf = merged;
702 0           inlen = c->tls_rbuf_len;
703             }
704 0           c->tls_rbuf = NULL;
705 0           c->tls_rbuf_len = 0;
706             }
707              
708 188 100         if (!c->tls_handshake_done) {
709             /* TLS handshake in progress */
710 64           size_t consumed = inlen;
711             ptls_buffer_t hsbuf;
712 64           ptls_buffer_init(&hsbuf, "", 0);
713              
714 64           int ret = ptls_handshake(c->tls, &hsbuf, inbuf, &consumed, NULL);
715              
716 64 100         if (hsbuf.off > 0) {
717 55 50         if (tls_wbuf_append(&c->tls_wbuf, hsbuf.base, hsbuf.off) != 0) {
718 0           trouble("TLS wbuf alloc failed during handshake fd=%d\n", c->fd);
719 0           ptls_buffer_dispose(&hsbuf);
720 0 0         if (merged) Safefree(merged);
721 0           safe_close_conn(c, "TLS allocation failure");
722 0           goto tls_read_cleanup;
723             }
724 55           int flush_ret = feer_tls_flush_wbuf(c);
725 55 50         if (flush_ret == -2) {
726 0           trouble("TLS flush error during handshake fd=%d\n", c->fd);
727 0           ptls_buffer_dispose(&hsbuf);
728 0 0         if (merged) Safefree(merged);
729 0           safe_close_conn(c, "TLS handshake flush error");
730 0           goto tls_read_cleanup;
731             }
732 55 50         if (flush_ret == -1 || c->tls_wbuf.off > 0) {
    50          
733             /* Need to wait for write readiness (EAGAIN or partial write) */
734 0           start_write_watcher(c);
735             }
736             }
737 64           ptls_buffer_dispose(&hsbuf);
738              
739 64 100         if (ret == 0) {
740             /* Handshake complete */
741 51           c->tls_handshake_done = 1;
742             trace("TLS handshake complete fd=%d\n", c->fd);
743              
744             /* Check ALPN result */
745 51           const char *proto = ptls_get_negotiated_protocol(c->tls);
746 51 50         if (proto && strlen(proto) == 2 && memcmp(proto, "h2", 2) == 0) {
    0          
    0          
747 0           c->tls_alpn_h2 = 1;
748             trace("TLS ALPN: h2 negotiated fd=%d\n", c->fd);
749             #ifdef FEERSUM_HAS_H2
750             feer_h2_init_session(c);
751             #endif
752             } else {
753             trace("TLS ALPN: http/1.1 (or none) fd=%d\n", c->fd);
754             }
755              
756             /* Process any remaining data after handshake */
757 51 50         if (consumed < inlen) {
758 0           size_t remaining = inlen - consumed;
759 0           uint8_t *extra = inbuf + consumed;
760              
761             #ifdef FEERSUM_HAS_H2
762             if (c->h2_session) {
763             /* Feed to nghttp2 */
764             ptls_buffer_t decbuf;
765             ptls_buffer_init(&decbuf, "", 0);
766             size_t dec_consumed = remaining;
767             int dec_ret = ptls_receive(c->tls, &decbuf, extra, &dec_consumed);
768             if (dec_ret != 0) {
769             ptls_buffer_dispose(&decbuf);
770             if (merged) Safefree(merged);
771             stop_all_watchers(c);
772             safe_close_conn(c, "TLS receive error (post-handshake H2)");
773             goto tls_read_cleanup;
774             }
775             if (dec_consumed < remaining) {
776             size_t leftover = remaining - dec_consumed;
777             Newx(c->tls_rbuf, leftover, uint8_t);
778             memcpy(c->tls_rbuf, extra + dec_consumed, leftover);
779             c->tls_rbuf_len = leftover;
780             }
781             if (decbuf.off > 0) {
782             feer_h2_session_recv(c, decbuf.base, decbuf.off);
783             }
784             ptls_buffer_dispose(&decbuf);
785             if (c->fd < 0) {
786             if (merged) Safefree(merged);
787             goto tls_read_cleanup;
788             }
789             /* Send any pending nghttp2 frames (SETTINGS etc.) */
790             feer_h2_session_send(c);
791             h2_check_stream_poll_cbs(aTHX_ c);
792             restart_read_timer(c);
793              
794             drain_h2_tls_records(c);
795              
796             if (merged) Safefree(merged);
797             goto tls_read_cleanup;
798             }
799             #endif
800             /* H1: decrypt and feed to HTTP parser */
801             ptls_buffer_t decbuf;
802 0           ptls_buffer_init(&decbuf, "", 0);
803 0           size_t dec_consumed = remaining;
804 0           int dec_ret = ptls_receive(c->tls, &decbuf, extra, &dec_consumed);
805 0 0         if (dec_ret != 0) {
806 0           ptls_buffer_dispose(&decbuf);
807 0 0         if (merged) Safefree(merged);
808 0           stop_all_watchers(c);
809 0           safe_close_conn(c, "TLS receive error (post-handshake H1)");
810 0           goto tls_read_cleanup;
811             }
812 0 0         if (dec_consumed < remaining) {
813 0           size_t leftover = remaining - dec_consumed;
814 0           Newx(c->tls_rbuf, leftover, uint8_t);
815 0           memcpy(c->tls_rbuf, extra + dec_consumed, leftover);
816 0           c->tls_rbuf_len = leftover;
817             }
818 0 0         if (decbuf.off > 0) {
819 0           size_t decrypted_len = decbuf.off;
820 0 0         if (!c->rbuf) {
821 0           c->rbuf = newSV_buf(decrypted_len + READ_BUFSZ);
822             }
823 0           sv_catpvn(c->rbuf, (const char *)decbuf.base, decrypted_len);
824 0           ptls_buffer_dispose(&decbuf);
825              
826 0           restart_read_timer(c);
827 0           int parse_ret = try_parse_http(c, decrypted_len);
828 0 0         if (parse_ret == -1) {
829 0           respond_with_server_error(c, "Malformed request\n", 400);
830 0           finish_receiving(c);
831 0 0         if (merged) Safefree(merged);
832 0           goto tls_read_cleanup;
833             }
834 0 0         if (parse_ret > 0) {
835 0 0         if (!process_request_headers(c, parse_ret))
836 0           finish_receiving(c);
837             }
838             /* Drain remaining tls_rbuf records so data from the same
839             * TCP segment as the handshake doesn't sit unprocessed.
840             * Handles both incomplete headers (parse_ret == -2) and
841             * body data following complete headers (parse_ret > 0). */
842 0 0         if (c->tls_rbuf_len > 0) {
843             ptls_buffer_t db;
844 0 0         while (c->tls_rbuf_len > 0) {
845 0           int drain_rv = feer_tls_drain_one_record(c, &db);
846 0 0         if (drain_rv < 0) break;
847 0 0         if (drain_rv == 1) continue;
848 0           sv_catpvn(c->rbuf, (const char *)db.base, db.off);
849 0           size_t added = db.off;
850 0           ptls_buffer_dispose(&db);
851 0 0         if (parse_ret == -2) {
852             /* Still parsing headers — feed to HTTP parser */
853 0           restart_read_timer(c);
854 0           int pr = try_parse_http(c, added);
855 0 0         if (pr == -1) {
856 0           respond_with_server_error(c, "Malformed request\n", 400);
857 0           finish_receiving(c);
858 0           break;
859             }
860 0 0         if (pr > 0) {
861 0 0         if (!process_request_headers(c, pr))
862 0           finish_receiving(c);
863 0           parse_ret = pr; /* headers now complete */
864             }
865             }
866             /* parse_ret > 0: body data appended to rbuf */
867             }
868             /* If headers are done and body was drained, check
869             * if the full body is now in rbuf. */
870 0 0         if (parse_ret > 0 && c->receiving == RECEIVE_BODY) {
    0          
871 0           c->received_cl = SvCUR(c->rbuf);
872 0 0         if (c->expected_cl > 0
873 0 0         && c->received_cl >= c->expected_cl) {
874 0           sched_request_callback(c);
875 0           finish_receiving(c);
876             }
877             }
878             }
879             } else {
880 0           ptls_buffer_dispose(&decbuf);
881             }
882             }
883 13 100         } else if (ret == PTLS_ERROR_IN_PROGRESS) {
884             /* Handshake still in progress, wait for more data */
885             trace("TLS handshake in progress fd=%d\n", c->fd);
886             /* Save unconsumed bytes (partial TLS handshake record) */
887 9 50         if (consumed < inlen) {
888 0           size_t leftover = inlen - consumed;
889 0           Newx(c->tls_rbuf, leftover, uint8_t);
890 0           memcpy(c->tls_rbuf, inbuf + consumed, leftover);
891 0           c->tls_rbuf_len = leftover;
892             }
893             } else {
894             /* Handshake error */
895             trace("TLS handshake error fd=%d ret=%d\n", c->fd, ret);
896 4           stop_all_watchers(c);
897 4           safe_close_conn(c, "TLS handshake error");
898             }
899 64 50         if (merged) Safefree(merged);
900 64           goto tls_read_cleanup;
901             }
902              
903             /* Handshake is done - decrypt application data */
904             {
905             ptls_buffer_t decbuf;
906 124           ptls_buffer_init(&decbuf, "", 0);
907 124           size_t consumed = inlen;
908 124           int ret = ptls_receive(c->tls, &decbuf, inbuf, &consumed);
909              
910             /* Save unconsumed bytes for next read (partial TLS record) */
911 124 50         if (ret == 0 && consumed < inlen) {
    50          
912 0           size_t remaining = inlen - consumed;
913 0           Newx(c->tls_rbuf, remaining, uint8_t);
914 0           memcpy(c->tls_rbuf, inbuf + consumed, remaining);
915 0           c->tls_rbuf_len = remaining;
916             }
917              
918 124 50         if (merged) Safefree(merged);
919              
920 124 50         if (ret != 0) {
921             trace("TLS receive error fd=%d ret=%d\n", c->fd, ret);
922             if (ret == PTLS_ALERT_CLOSE_NOTIFY) {
923             trace("TLS close_notify fd=%d\n", c->fd);
924             }
925 0           ptls_buffer_dispose(&decbuf);
926 0           change_receiving_state(c, RECEIVE_SHUTDOWN);
927 0           stop_all_watchers(c);
928 0           safe_close_conn(c, "TLS receive error");
929 64           goto tls_read_cleanup;
930             }
931              
932 124 100         if (decbuf.off == 0) {
933 51           ptls_buffer_dispose(&decbuf);
934 51           goto tls_read_cleanup; /* No application data yet */
935             }
936              
937             #ifdef FEERSUM_HAS_H2
938             if (c->h2_session) {
939             /* Feed decrypted data to nghttp2 */
940             feer_h2_session_recv(c, decbuf.base, decbuf.off);
941             ptls_buffer_dispose(&decbuf);
942             if (c->fd < 0) goto tls_read_cleanup;
943             /* Send any pending nghttp2 frames */
944             feer_h2_session_send(c);
945             h2_check_stream_poll_cbs(aTHX_ c);
946             restart_read_timer(c);
947              
948             drain_h2_tls_records(c);
949              
950             goto tls_read_cleanup;
951             }
952             #endif
953              
954             /* TLS tunnel: relay decrypted data to sv[0] for the app */
955 73 100         if (c->tls_tunnel) {
956 13 50         if (tls_tunnel_write_or_buffer(c, (const char *)decbuf.base, decbuf.off) < 0) {
957 0           ptls_buffer_dispose(&decbuf);
958 0           stop_all_watchers(c);
959 0           safe_close_conn(c, "TLS tunnel write error");
960 0           change_responding_state(c, RESPOND_SHUTDOWN);
961 0           goto tls_read_cleanup;
962             }
963 13           ptls_buffer_dispose(&decbuf);
964              
965             /* Drain remaining TLS records from tls_rbuf into the tunnel.
966             * Multiple records may have been read in one syscall above;
967             * without draining, they sit in tls_rbuf until new socket data
968             * arrives — deadlocking if the remote is waiting for a reply. */
969 13           drain_tls_tunnel_rbuf(c);
970              
971 13           goto tls_read_cleanup;
972             }
973              
974             /* HTTP/1.1 over TLS: append decrypted data to rbuf */
975 60           got_n = (ssize_t)decbuf.off;
976 60 100         if (!c->rbuf) {
977 51           c->rbuf = newSV_buf(got_n + READ_BUFSZ);
978             }
979 60           sv_catpvn(c->rbuf, (const char *)decbuf.base, decbuf.off);
980 60           ptls_buffer_dispose(&decbuf);
981              
982             /* Drain remaining TLS records from tls_rbuf */
983             {
984             ptls_buffer_t db;
985             int drain_rv;
986 60 50         while ((drain_rv = feer_tls_drain_one_record(c, &db)) >= 0) {
987 0 0         if (drain_rv == 1) continue; /* non-data TLS record */
988 0           got_n += (ssize_t)db.off;
989 0           sv_catpvn(c->rbuf, (const char *)db.base, db.off);
990 0           ptls_buffer_dispose(&db);
991             }
992             }
993             }
994 60           goto tls_parse;
995              
996 1           tls_pipelined:
997 1           got_n = c->pipelined;
998 1           c->pipelined = 0;
999              
1000 61           tls_parse:
1001 61           restart_read_timer(c);
1002 61 100         if (c->receiving == RECEIVE_WAIT)
1003 8           change_receiving_state(c, RECEIVE_HEADERS);
1004              
1005 61 100         if (likely(c->receiving <= RECEIVE_HEADERS)) {
1006 59           int parse_ret = try_parse_http(c, (size_t)got_n);
1007 59 50         if (parse_ret == -1) {
1008 0           respond_with_server_error(c, "Malformed request\n", 400);
1009 0           finish_receiving(c);
1010 0           goto tls_read_cleanup;
1011             }
1012 59 50         if (parse_ret == -2) {
1013             /* Incomplete, wait for more data (read watcher already active) */
1014 0           goto tls_read_cleanup;
1015             }
1016             /* Headers complete. parse_ret = body offset */
1017 59 100         if (!process_request_headers(c, parse_ret))
1018 57           finish_receiving(c);
1019             }
1020 2 50         else if (likely(c->receiving == RECEIVE_BODY)) {
1021 2           c->received_cl += got_n;
1022 2 50         if (c->received_cl >= c->expected_cl) {
1023 2           sched_request_callback(c);
1024 2           finish_receiving(c);
1025             }
1026             }
1027 0 0         else if (c->receiving == RECEIVE_CHUNKED) {
1028 0           int ret = try_parse_chunked(c);
1029 0 0         if (ret == -1) {
1030 0           respond_with_server_error(c, "Malformed chunked encoding\n", 400);
1031 0           finish_receiving(c);
1032             }
1033 0 0         else if (ret == 0) {
1034 0           sched_request_callback(c);
1035 0           finish_receiving(c);
1036             }
1037             /* ret == 1: need more data, watcher stays active */
1038             }
1039 0 0         else if (c->receiving == RECEIVE_STREAMING) {
1040 0           c->received_cl += got_n;
1041 0 0         if (c->poll_read_cb) {
1042 0           call_poll_callback(c, 0);
1043             }
1044 0 0         if (c->receiving >= RECEIVE_SHUTDOWN) {
1045 0           finish_receiving(c);
1046 0           goto tls_read_cleanup;
1047             }
1048 0 0         if (c->expected_cl > 0 && c->received_cl >= c->expected_cl)
    0          
1049 0           finish_receiving(c);
1050             }
1051              
1052 0           tls_read_cleanup:
1053 348           SvREFCNT_dec(c->self);
1054 348           }
1055              
1056             /*
1057             * try_tls_conn_write - libev write callback for TLS connections.
1058             *
1059             * Encrypts pending response data via ptls and writes to socket.
1060             * Also handles sendfile-over-TLS (pread + encrypt + write).
1061             */
1062             static void
1063 60           try_tls_conn_write(EV_P_ ev_io *w, int revents)
1064             {
1065 60           struct feer_conn *c = (struct feer_conn *)w->data;
1066             PERL_UNUSED_VAR(revents);
1067             PERL_UNUSED_VAR(loop);
1068              
1069             dTHX;
1070 60           SvREFCNT_inc_void_NN(c->self); /* prevent premature free during callback */
1071             trace("tls_conn_write fd=%d\n", c->fd);
1072              
1073 60 50         if (unlikely(!c->tls)) {
1074 0           trouble("tls_conn_write: no TLS context fd=%d\n", c->fd);
1075 0           stop_write_watcher(c);
1076 0           goto tls_write_cleanup;
1077             }
1078              
1079             /* First, flush any pending encrypted data from TLS handshake or previous writes */
1080 60 50         if (c->tls_wbuf.off > 0) {
1081 0           int flush_ret = feer_tls_flush_wbuf(c);
1082 0 0         if (flush_ret == -1) goto tls_write_cleanup; /* EAGAIN, keep write watcher active */
1083 0 0         if (flush_ret == -2) goto tls_write_error;
1084             /* If there's still data after partial flush, keep trying */
1085 0 0         if (c->tls_wbuf.off > 0) goto tls_write_cleanup;
1086             }
1087              
1088             #ifdef FEERSUM_HAS_H2
1089             if (c->h2_session) {
1090             /* For H2, nghttp2 manages the write buffer.
1091             * Call session_send to generate frames, encrypt, and write. */
1092             feer_h2_session_send(c);
1093             h2_check_stream_poll_cbs(aTHX_ c);
1094             if (c->tls_wbuf.off > 0) {
1095             int flush_ret = feer_tls_flush_wbuf(c);
1096             if (flush_ret == -1) goto tls_write_cleanup;
1097             if (flush_ret == -2) goto tls_write_error;
1098             }
1099             if (c->tls_wbuf.off == 0) {
1100             stop_write_watcher(c);
1101             }
1102             goto tls_write_cleanup;
1103             }
1104             #endif
1105              
1106             /* HTTP/1.1 over TLS: encrypt wbuf_rinq (headers/body) first, then sendfile */
1107              
1108             /* Pre-encryption low-water check: if buffer is below threshold, let
1109             * poll_cb refill before we start encrypting (matches H1 plain path). */
1110 60 50         if (c->wbuf_rinq && c->cached_wbuf_low_water > 0
    100          
1111 1 50         && c->wbuf_len <= c->cached_wbuf_low_water
1112 1 50         && c->responding == RESPOND_STREAMING && c->poll_write_cb) {
    50          
1113 1 50         if (c->poll_write_cb_is_io_handle)
1114 0           pump_io_handle(c);
1115             else
1116 1           call_poll_callback(c, 1);
1117             }
1118              
1119             /* Encrypt data from wbuf_rinq (must come before sendfile to send headers first) */
1120 60 50         if (c->wbuf_rinq) {
1121             struct iomatrix *m;
1122 123 100         while ((m = (struct iomatrix *)rinq_shift(&c->wbuf_rinq)) != NULL) {
1123             unsigned int i;
1124 128 100         for (i = 0; i < m->count; i++) {
1125 65 50         if (m->iov[i].iov_len == 0) continue;
1126              
1127 65           c->wbuf_len -= m->iov[i].iov_len;
1128              
1129             ptls_buffer_t encbuf;
1130 65           ptls_buffer_init(&encbuf, "", 0);
1131 65           int ret = ptls_send(c->tls, &encbuf,
1132 65           m->iov[i].iov_base, m->iov[i].iov_len);
1133 65 50         if (ret != 0) {
1134             unsigned int j;
1135 0           ptls_buffer_dispose(&encbuf);
1136 0           trouble("ptls_send error fd=%d ret=%d\n", c->fd, ret);
1137 0 0         for (j = 0; j < m->count; j++) {
1138 0 0         if (m->sv[j]) SvREFCNT_dec(m->sv[j]);
1139             }
1140 0 0         IOMATRIX_FREE(m);
1141 0           goto tls_write_error;
1142             }
1143 65 50         if (encbuf.off > 0) {
1144 65 50         if (tls_wbuf_append(&c->tls_wbuf, encbuf.base, encbuf.off) != 0) {
1145             unsigned int j;
1146 0           ptls_buffer_dispose(&encbuf);
1147 0           trouble("TLS wbuf alloc failed fd=%d\n", c->fd);
1148 0 0         for (j = 0; j < m->count; j++) {
1149 0 0         if (m->sv[j]) SvREFCNT_dec(m->sv[j]);
1150             }
1151 0 0         IOMATRIX_FREE(m);
1152 0           goto tls_write_error;
1153             }
1154             }
1155 65           ptls_buffer_dispose(&encbuf);
1156             }
1157              
1158             /* Free the iomatrix SVs */
1159 128 100         for (i = 0; i < m->count; i++) {
1160 65 50         if (m->sv[i]) SvREFCNT_dec(m->sv[i]);
1161             }
1162 63 50         IOMATRIX_FREE(m);
1163              
1164             /* Low-water-mark: fire poll_cb to refill before encrypting more */
1165 63 100         if (c->cached_wbuf_low_water > 0
1166 4 50         && c->wbuf_len <= c->cached_wbuf_low_water
1167 4 100         && c->responding == RESPOND_STREAMING && c->poll_write_cb) {
    50          
1168 3 50         if (c->poll_write_cb_is_io_handle)
1169 0           pump_io_handle(c);
1170             else
1171 3           call_poll_callback(c, 1);
1172             /* poll_cb may have added more data — loop continues */
1173             }
1174             }
1175              
1176             /* Flush all encrypted data */
1177 60           int flush_ret = feer_tls_flush_wbuf(c);
1178 60 50         if (flush_ret == -1) goto tls_write_cleanup; /* EAGAIN */
1179 60 50         if (flush_ret == -2) goto tls_write_error;
1180 60 50         if (flush_ret > 0) restart_write_timer(c);
1181             }
1182              
1183             /* Handle sendfile over TLS: pread + encrypt + write */
1184 60 100         if (c->sendfile_fd >= 0 && c->sendfile_remain > 0) {
    50          
1185             uint8_t filebuf[TLS_RAW_BUFSZ];
1186 2           size_t to_read = c->sendfile_remain;
1187 2 50         if (to_read > sizeof(filebuf)) to_read = sizeof(filebuf);
1188              
1189 2           ssize_t file_nread = pread(c->sendfile_fd, filebuf, to_read, c->sendfile_off);
1190 2 50         if (file_nread < 0) {
1191 0 0         if (errno == EINTR)
1192 0           goto tls_write_cleanup; /* retry on next watcher fire */
1193 0           trouble("TLS pread(sendfile_fd) fd=%d: %s\n", c->fd, strerror(errno));
1194 0 0         CLOSE_SENDFILE_FD(c);
    0          
1195 0           change_responding_state(c, RESPOND_SHUTDOWN);
1196 2           goto tls_write_finished;
1197             }
1198 2 50         if (file_nread == 0) {
1199 0 0         CLOSE_SENDFILE_FD(c);
    0          
1200 0           change_responding_state(c, RESPOND_SHUTDOWN);
1201 0           goto tls_write_finished;
1202             }
1203              
1204             /* Encrypt file data */
1205             ptls_buffer_t encbuf;
1206 2           ptls_buffer_init(&encbuf, "", 0);
1207 2           int ret = ptls_send(c->tls, &encbuf, filebuf, file_nread);
1208 2 50         if (ret != 0) {
1209 0           ptls_buffer_dispose(&encbuf);
1210 0           trouble("ptls_send(sendfile) error fd=%d ret=%d\n", c->fd, ret);
1211 0 0         CLOSE_SENDFILE_FD(c);
    0          
1212 0           goto tls_write_error;
1213             }
1214              
1215             /* Queue encrypted data */
1216 2 50         if (encbuf.off > 0) {
1217 2 50         if (tls_wbuf_append(&c->tls_wbuf, encbuf.base, encbuf.off) != 0) {
1218 0           ptls_buffer_dispose(&encbuf);
1219 0           trouble("TLS wbuf alloc failed (sendfile) fd=%d\n", c->fd);
1220 0 0         CLOSE_SENDFILE_FD(c);
    0          
1221 0           goto tls_write_error;
1222             }
1223             }
1224 2           ptls_buffer_dispose(&encbuf);
1225              
1226 2           c->sendfile_off += file_nread;
1227 2           c->sendfile_remain -= file_nread;
1228              
1229 2 50         if (c->sendfile_remain == 0) {
1230 2 50         CLOSE_SENDFILE_FD(c);
    50          
1231 2           change_responding_state(c, RESPOND_SHUTDOWN);
1232             }
1233              
1234             /* Flush encrypted data */
1235             {
1236 2           int sf_flush_ret = feer_tls_flush_wbuf(c);
1237 2 50         if (sf_flush_ret == -1) goto tls_write_cleanup; /* EAGAIN */
1238 2 50         if (sf_flush_ret == -2) goto tls_write_error;
1239             }
1240 2 50         if (c->sendfile_remain > 0 || c->tls_wbuf.off > 0)
    50          
1241 0           goto tls_write_cleanup; /* More to send, keep watcher active */
1242 2           goto tls_write_finished;
1243             }
1244              
1245 58           tls_write_finished:
1246 60 50         if ((!c->wbuf_rinq || (c->cached_wbuf_low_water > 0
    0          
1247 0 0         && c->wbuf_len <= c->cached_wbuf_low_water))
1248 60 50         && c->sendfile_fd < 0 && c->tls_wbuf.off == 0) {
    50          
1249 60 100         if (c->responding == RESPOND_SHUTDOWN || c->responding == RESPOND_NORMAL) {
    50          
1250 50           handle_keepalive_or_close(c, try_tls_conn_read);
1251 10 50         } else if (c->responding == RESPOND_STREAMING && c->poll_write_cb) {
    50          
1252 10 50         if (c->poll_write_cb_is_io_handle)
1253 0           pump_io_handle(c);
1254             else
1255 10           call_poll_callback(c, 1 /* is_write */);
1256 0 0         } else if (c->responding == RESPOND_STREAMING) {
1257 0           stop_write_watcher(c);
1258 0           stop_write_timer(c);
1259 0 0         } else if (c->responding == RESPOND_NOT_STARTED) {
1260             /* tls_wbuf drained — nothing else to write. Covers both
1261             * TLS tunnel (io() called, no HTTP response) and handshake
1262             * flush (EAGAIN during handshake, now completed). */
1263 0           stop_write_watcher(c);
1264             }
1265             }
1266 60           goto tls_write_cleanup;
1267              
1268 0           tls_write_error:
1269 0           stop_all_watchers(c);
1270 0           change_responding_state(c, RESPOND_SHUTDOWN);
1271 0           safe_close_conn(c, "TLS write error");
1272              
1273 60           tls_write_cleanup:
1274 60           SvREFCNT_dec(c->self);
1275 60           }
1276              
1277             /*
1278             * Encrypt response data and queue for TLS writing.
1279             * Returns 0 on success, -1 on error.
1280             */
1281             static int
1282 23           feer_tls_send(struct feer_conn *c, const void *data, size_t len)
1283             {
1284 23 50         if (!c->tls || len == 0) return 0;
    50          
1285              
1286             ptls_buffer_t encbuf;
1287 23           ptls_buffer_init(&encbuf, "", 0);
1288 23           int ret = ptls_send(c->tls, &encbuf, data, len);
1289 23 50         if (ret != 0) {
1290 0           ptls_buffer_dispose(&encbuf);
1291 0           trouble("feer_tls_send error fd=%d ret=%d\n", c->fd, ret);
1292 0           return -1;
1293             }
1294 23 50         if (encbuf.off > 0) {
1295 23 50         if (tls_wbuf_append(&c->tls_wbuf, encbuf.base, encbuf.off) != 0) {
1296 0           ptls_buffer_dispose(&encbuf);
1297 0           trouble("TLS wbuf alloc failed (send) fd=%d\n", c->fd);
1298 0           return -1;
1299             }
1300             }
1301 23           ptls_buffer_dispose(&encbuf);
1302 23           return 0;
1303             }
1304              
1305             #endif /* FEERSUM_HAS_TLS */