LCOV - code coverage report
Current view: top level - waltz/tlsrec - fd_tlsrec_sock.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 15 26 57.7 %
Date: 2026-09-17 04:28:31 Functions: 7 102 6.9 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_waltz_tlsrec_fd_tlsrec_sock_h
       2             : #define HEADER_fd_src_waltz_tlsrec_fd_tlsrec_sock_h
       3             : 
       4             : /* fd_tlsrec_sock shuttles TLS records between a non-blocking TCP
       5             :    socket and an fd_tlsrec_conn.  It owns the two buffers every socket
       6             :    user of fd_tlsrec needs: ciphertext that the socket did not accept
       7             :    yet, and plaintext that the application did not consume yet.
       8             : 
       9             :    The socket is never read while plaintext is still held (it would be
      10             :    overwritten), and never read while the ciphertext buffer lacks room
      11             :    for the replies fd_tlsrec_conn_rx may generate (a KeyUpdate).  In
      12             :    both cases the TCP window back-pressures the peer.  A send parked on
      13             :    EAGAIN alone does not stop RX: whatever conn_rx emits is appended
      14             :    behind the parked bytes.
      15             : 
      16             :    All functions use MSG_DONTWAIT|MSG_NOSIGNAL and never block. */
      17             : 
      18             : #include "fd_tlsrec.h"
      19             : 
      20             : /* FD_TLSREC_SOCK_TX_RESERVE is the ciphertext headroom kept free for
      21             :    conn_rx output while a record is parked.  Post-handshake, one conn_rx
      22             :    call emits at most one 27 byte KeyUpdate record however many updates
      23             :    the peer requested.  FD_TLSREC_SOCK_RX_MTU is the most ciphertext
      24             :    read from the socket per call. */
      25             : 
      26             : #define FD_TLSREC_SOCK_TX_RESERVE (512UL)
      27             : #define FD_TLSREC_SOCK_TX_BUF_SZ  (FD_TLSREC_PLAINTEXT_MAX+22UL+FD_TLSREC_SOCK_TX_RESERVE)
      28             : #define FD_TLSREC_SOCK_RX_MTU     (FD_TLSREC_CAP)
      29             : #define FD_TLSREC_SOCK_RX_BUF_SZ  (FD_TLSREC_CAP+FD_TLSREC_SOCK_RX_MTU)
      30             : 
      31             : struct fd_tlsrec_sock {
      32             :   uchar tx_buf[ FD_TLSREC_SOCK_TX_BUF_SZ ];  /* ciphertext [tx_off,tx_sz) awaits send(2) */
      33             :   ulong tx_off;
      34             :   ulong tx_sz;
      35             : 
      36             :   uchar rx_buf[ FD_TLSREC_SOCK_RX_BUF_SZ ];  /* plaintext [rx_off,rx_sz) awaits the app */
      37             :   ulong rx_off;
      38             :   ulong rx_sz;
      39             : };
      40             : 
      41             : typedef struct fd_tlsrec_sock fd_tlsrec_sock_t;
      42             : 
      43             : /* Error codes.  Positive values are FD_TLSREC_ERR_{...}. */
      44             : 
      45           0 : #define FD_TLSREC_SOCK_ERR_RECV (-1)  /* recv(2) failed, errno set */
      46           0 : #define FD_TLSREC_SOCK_ERR_SEND (-2)  /* send(2) failed, errno set */
      47           9 : #define FD_TLSREC_SOCK_ERR_EOF  (-3)  /* peer closed the connection */
      48             : 
      49             : FD_PROTOTYPES_BEGIN
      50             : 
      51             : static inline void
      52         135 : fd_tlsrec_sock_init( fd_tlsrec_sock_t * sock ) {
      53         135 :   sock->tx_off = 0UL; sock->tx_sz = 0UL;
      54         135 :   sock->rx_off = 0UL; sock->rx_sz = 0UL;
      55         135 : }
      56             : 
      57             : FD_FN_PURE static inline int
      58           0 : fd_tlsrec_sock_tx_pending( fd_tlsrec_sock_t const * sock ) {
      59           0 :   return sock->tx_off!=sock->tx_sz;
      60           0 : }
      61             : 
      62             : /* Plaintext accessors: fd_tlsrec_sock_rx_avail is the number of
      63             :    decrypted bytes held, fd_tlsrec_sock_rx_data points to them, and
      64             :    fd_tlsrec_sock_rx_consume releases the first sz of them.
      65             :    fd_tlsrec_sock_rx_pop copies up to dst_max bytes out and returns the
      66             :    count. */
      67             : 
      68             : FD_FN_PURE static inline ulong
      69          21 : fd_tlsrec_sock_rx_avail( fd_tlsrec_sock_t const * sock ) {
      70          21 :   return sock->rx_sz - sock->rx_off;
      71          21 : }
      72             : 
      73             : FD_FN_PURE static inline uchar const *
      74           3 : fd_tlsrec_sock_rx_data( fd_tlsrec_sock_t const * sock ) {
      75           3 :   return sock->rx_buf + sock->rx_off;
      76           3 : }
      77             : 
      78             : static inline void
      79             : fd_tlsrec_sock_rx_consume( fd_tlsrec_sock_t * sock,
      80           3 :                            ulong              sz ) {
      81           3 :   sock->rx_off += sz;
      82           3 :   if( sock->rx_off==sock->rx_sz ) { sock->rx_off = 0UL; sock->rx_sz = 0UL; }
      83           3 : }
      84             : 
      85             : static inline ulong
      86             : fd_tlsrec_sock_rx_pop( fd_tlsrec_sock_t * sock,
      87             :                        void *             dst,
      88           0 :                        ulong              dst_max ) {
      89           0 :   ulong sz = fd_ulong_min( fd_tlsrec_sock_rx_avail( sock ), dst_max );
      90           0 :   fd_memcpy( dst, fd_tlsrec_sock_rx_data( sock ), sz );
      91           0 :   fd_tlsrec_sock_rx_consume( sock, sz );
      92           0 :   return sz;
      93           0 : }
      94             : 
      95             : /* fd_tlsrec_sock_flush writes parked ciphertext to fd until the buffer
      96             :    is empty or send blocks.  Returns 0 if the buffer is now empty, 1 if
      97             :    send would block (bytes remain, wait for POLLOUT), and
      98             :    FD_TLSREC_SOCK_ERR_SEND on a hard send error. */
      99             : 
     100             : int
     101             : fd_tlsrec_sock_flush( fd_tlsrec_sock_t * sock,
     102             :                       int                fd );
     103             : 
     104             : /* fd_tlsrec_sock_rx reads up to FD_TLSREC_SOCK_RX_MTU bytes of
     105             :    ciphertext from fd, decrypts them into the plaintext buffer, and
     106             :    sends whatever conn emitted in response (handshake flights, a
     107             :    KeyUpdate reply).  conn is stepped even if nothing was read, which
     108             :    is what produces the ClientHello.  The read is skipped, and the call
     109             :    is a no-op, while plaintext is held or the ciphertext buffer lacks
     110             :    FD_TLSREC_SOCK_TX_RESERVE bytes of headroom.
     111             : 
     112             :    Returns 0 on success with *opt_rx_sz set to the number of ciphertext
     113             :    bytes read (0 on EAGAIN or a skipped read).  Otherwise returns
     114             :    FD_TLSREC_SOCK_ERR_{RECV,SEND,EOF} or a positive FD_TLSREC_ERR code;
     115             :    the connection should be dropped.  ERR_EOF is also returned for a
     116             :    TLS close_notify, once the plaintext preceding it was consumed. */
     117             : 
     118             : int
     119             : fd_tlsrec_sock_rx( fd_tlsrec_sock_t * sock,
     120             :                    fd_tlsrec_conn_t * conn,
     121             :                    int                fd,
     122             :                    ulong *            opt_rx_sz );
     123             : 
     124             : /* fd_tlsrec_sock_close queues a close_notify alert behind any parked
     125             :    ciphertext and flushes.  Returns like fd_tlsrec_sock_flush: 0 when
     126             :    everything was sent, 1 if bytes remain (wait for POLLOUT, then flush),
     127             :    FD_TLSREC_SOCK_ERR_SEND, or a positive FD_TLSREC_ERR code (ERR_STATE
     128             :    if the connection is not established or already closed for writing,
     129             :    ERR_OOM if the parked bytes leave no room, in which case flush first). */
     130             : 
     131             : int
     132             : fd_tlsrec_sock_close( fd_tlsrec_sock_t * sock,
     133             :                       fd_tlsrec_conn_t * conn,
     134             :                       int                fd );
     135             : 
     136             : /* fd_tlsrec_sock_tx encrypts one record of application data from
     137             :    [app,app+app_sz) and sends it, parking any tail the socket did not
     138             :    accept.  Does nothing if ciphertext is already parked (flush first).
     139             :    *opt_consumed is set to the number of application bytes encrypted,
     140             :    at most FD_TLSREC_PLAINTEXT_MAX.  Returns 0 on success,
     141             :    FD_TLSREC_SOCK_ERR_SEND, or a positive FD_TLSREC_ERR code. */
     142             : 
     143             : int
     144             : fd_tlsrec_sock_tx( fd_tlsrec_sock_t * sock,
     145             :                    fd_tlsrec_conn_t * conn,
     146             :                    int                fd,
     147             :                    void const *       app,
     148             :                    ulong              app_sz,
     149             :                    ulong *            opt_consumed );
     150             : 
     151             : FD_FN_CONST char const *
     152             : fd_tlsrec_sock_strerror( int err );
     153             : 
     154             : FD_PROTOTYPES_END
     155             : 
     156             : #endif /* HEADER_fd_src_waltz_tlsrec_fd_tlsrec_sock_h */

Generated by: LCOV version 1.14