LCOV - code coverage report
Current view: top level - waltz/openssl - fd_openssl.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 112 0.0 %
Date: 2026-03-19 18:19:27 Functions: 0 10 0.0 %

          Line data    Source code
       1             : #include "fd_openssl.h"
       2             : 
       3             : #if !FD_HAS_OPENSSL
       4             : #error "fd_openssl.c requires FD_HAS_OPENSSL"
       5             : #endif
       6             : 
       7             : #include "../../util/log/fd_log.h"
       8             : 
       9             : #include <openssl/ssl.h>
      10             : #include <openssl/bio.h>
      11             : #include <sys/socket.h>
      12             : #include <unistd.h>
      13             : #include <string.h>
      14             : #include <errno.h>
      15             : 
      16             : FD_FN_CONST char const *
      17           0 : fd_openssl_ssl_strerror( int ssl_err ) {
      18           0 :   switch( ssl_err ) {
      19           0 :   case SSL_ERROR_NONE:                 return "SSL_ERROR_NONE";
      20           0 :   case SSL_ERROR_SSL:                  return "SSL_ERROR_SSL";
      21           0 :   case SSL_ERROR_WANT_READ:            return "SSL_ERROR_WANT_READ";
      22           0 :   case SSL_ERROR_WANT_WRITE:           return "SSL_ERROR_WANT_WRITE";
      23           0 :   case SSL_ERROR_WANT_X509_LOOKUP:     return "SSL_ERROR_WANT_X509_LOOKUP";
      24           0 :   case SSL_ERROR_SYSCALL:              return "SSL_ERROR_SYSCALL";
      25           0 :   case SSL_ERROR_ZERO_RETURN:          return "SSL_ERROR_ZERO_RETURN";
      26           0 :   case SSL_ERROR_WANT_CONNECT:         return "SSL_ERROR_WANT_CONNECT";
      27           0 :   case SSL_ERROR_WANT_ACCEPT:          return "SSL_ERROR_WANT_ACCEPT";
      28           0 :   case SSL_ERROR_WANT_ASYNC:           return "SSL_ERROR_WANT_ASYNC";
      29           0 :   case SSL_ERROR_WANT_ASYNC_JOB:       return "SSL_ERROR_WANT_ASYNC_JOB";
      30           0 :   case SSL_ERROR_WANT_CLIENT_HELLO_CB: return "SSL_ERROR_WANT_CLIENT_HELLO_CB";
      31           0 :   case SSL_ERROR_WANT_RETRY_VERIFY:    return "SSL_ERROR_WANT_RETRY_VERIFY";
      32           0 :   default: return "unknown";
      33           0 :   }
      34           0 : }
      35             : 
      36             : /* Custom BIO method that uses send(MSG_NOSIGNAL) instead of write()
      37             :    to prevent SIGPIPE on broken TCP connections.
      38             : 
      39             :    We implement all callbacks ourselves rather than copying them from
      40             :    BIO_s_socket() with the deprecated BIO_meth_get_* functions. */
      41             : 
      42             : struct fd_bio_sock_data {
      43             :   int fd;
      44             :   int close_flag;
      45             : };
      46             : 
      47             : static int
      48           0 : fd_bio_nosigpipe_create( BIO * bio ) {
      49           0 :   struct fd_bio_sock_data * data = OPENSSL_zalloc( sizeof(struct fd_bio_sock_data) );
      50           0 :   if( FD_UNLIKELY( !data ) ) return 0;
      51           0 :   data->fd         = -1;
      52           0 :   data->close_flag = BIO_NOCLOSE;
      53           0 :   BIO_set_data( bio, data );
      54           0 :   return 1;
      55           0 : }
      56             : 
      57             : static int
      58           0 : fd_bio_nosigpipe_destroy( BIO * bio ) {
      59           0 :   struct fd_bio_sock_data * data = BIO_get_data( bio );
      60           0 :   if( FD_UNLIKELY( !data ) ) return 0;
      61           0 :   if( data->close_flag==BIO_CLOSE && data->fd>=0 ) {
      62           0 :     close( data->fd );
      63           0 :     data->fd = -1;
      64           0 :   }
      65           0 :   OPENSSL_free( data );
      66           0 :   BIO_set_data( bio, NULL );
      67           0 :   BIO_set_init( bio, 0 );
      68           0 :   return 1;
      69           0 : }
      70             : 
      71             : static int
      72             : fd_bio_nosigpipe_write( BIO *        bio,
      73             :                         char const * buf,
      74           0 :                         int          len ) {
      75           0 :   struct fd_bio_sock_data * data = BIO_get_data( bio );
      76           0 :   if( FD_UNLIKELY( !data || data->fd<0 ) ) return -1;
      77           0 :   if( FD_UNLIKELY( len<=0 ) ) return 0;
      78             : 
      79           0 :   BIO_clear_retry_flags( bio );
      80           0 :   int ret = (int)sendto( data->fd, buf, (size_t)len, MSG_NOSIGNAL, NULL, 0 );
      81           0 :   if( ret<=0 && BIO_sock_should_retry( ret ) ) {
      82           0 :     BIO_set_retry_write( bio );
      83           0 :   }
      84           0 :   return ret;
      85           0 : }
      86             : 
      87             : static int
      88             : fd_bio_nosigpipe_read( BIO *  bio,
      89             :                        char * buf,
      90           0 :                        int    len ) {
      91           0 :   struct fd_bio_sock_data * data = BIO_get_data( bio );
      92           0 :   if( FD_UNLIKELY( !data || data->fd<0 ) ) return -1;
      93             : 
      94           0 :   BIO_clear_retry_flags( bio );
      95           0 :   int ret = (int)read( data->fd, buf, (ulong)len );
      96           0 :   if( ret<=0 && BIO_sock_should_retry( ret ) ) {
      97           0 :     BIO_set_retry_read( bio );
      98           0 :   }
      99           0 :   return ret;
     100           0 : }
     101             : 
     102             : static long
     103             : fd_bio_nosigpipe_ctrl( BIO *  bio,
     104             :                        int    cmd,
     105             :                        long   num,
     106           0 :                        void * ptr ) {
     107           0 :   struct fd_bio_sock_data * data = BIO_get_data( bio );
     108           0 :   if( FD_UNLIKELY( !data ) ) return 0;
     109             : 
     110           0 :   switch( cmd ) {
     111           0 :   case BIO_C_SET_FD:
     112           0 :     if( data->close_flag==BIO_CLOSE && data->fd>=0 ) close( data->fd );
     113           0 :     data->fd         = *(int *)ptr;
     114           0 :     data->close_flag = (int)num;
     115           0 :     BIO_set_init( bio, (data->fd>=0) );
     116           0 :     return 1;
     117           0 :   case BIO_C_GET_FD:
     118           0 :     if( data->fd<0 ) return -1;
     119           0 :     if( ptr ) *(int *)ptr = data->fd;
     120           0 :     return (long)data->fd;
     121           0 :   case BIO_CTRL_GET_CLOSE:
     122           0 :     return (long)data->close_flag;
     123           0 :   case BIO_CTRL_SET_CLOSE:
     124           0 :     data->close_flag = (int)num;
     125           0 :     return 1;
     126           0 :   case BIO_CTRL_FLUSH:
     127           0 :     return 1;
     128           0 :   default:
     129           0 :     return 0;
     130           0 :   }
     131           0 : }
     132             : 
     133             : static int
     134             : fd_bio_nosigpipe_puts( BIO *        bio,
     135           0 :                        char const * str ) {
     136           0 :   return fd_bio_nosigpipe_write( bio, str, (int)strlen( str ) );
     137           0 : }
     138             : 
     139             : static BIO_METHOD * fd_bio_nosigpipe_method_ptr;
     140             : 
     141             : static void
     142           0 : fd_bio_nosigpipe_method_init( void ) {
     143           0 :   BIO_METHOD * method = BIO_meth_new( BIO_TYPE_SOCKET, "socket(nosigpipe)" );
     144           0 :   if( FD_UNLIKELY( !method ) ) FD_LOG_ERR(( "BIO_meth_new failed" ));
     145             : 
     146           0 :   if( FD_UNLIKELY( !BIO_meth_set_write  ( method, fd_bio_nosigpipe_write   ) ) ) FD_LOG_ERR(( "BIO_meth_set_write failed" ));
     147           0 :   if( FD_UNLIKELY( !BIO_meth_set_read   ( method, fd_bio_nosigpipe_read    ) ) ) FD_LOG_ERR(( "BIO_meth_set_read failed" ));
     148           0 :   if( FD_UNLIKELY( !BIO_meth_set_puts   ( method, fd_bio_nosigpipe_puts    ) ) ) FD_LOG_ERR(( "BIO_meth_set_puts failed" ));
     149           0 :   if( FD_UNLIKELY( !BIO_meth_set_ctrl   ( method, fd_bio_nosigpipe_ctrl    ) ) ) FD_LOG_ERR(( "BIO_meth_set_ctrl failed" ));
     150           0 :   if( FD_UNLIKELY( !BIO_meth_set_create ( method, fd_bio_nosigpipe_create  ) ) ) FD_LOG_ERR(( "BIO_meth_set_create failed" ));
     151           0 :   if( FD_UNLIKELY( !BIO_meth_set_destroy( method, fd_bio_nosigpipe_destroy ) ) ) FD_LOG_ERR(( "BIO_meth_set_destroy failed" ));
     152             : 
     153           0 :   fd_bio_nosigpipe_method_ptr = method;
     154           0 : }
     155             : 
     156             : static BIO_METHOD *
     157           0 : fd_bio_nosigpipe_method( void ) {
     158           0 :   FD_ONCE_BEGIN {
     159           0 :     fd_bio_nosigpipe_method_init();
     160           0 :   } FD_ONCE_END;
     161           0 :   return fd_bio_nosigpipe_method_ptr;
     162           0 : }
     163             : 
     164             : BIO *
     165             : fd_openssl_bio_new_socket( int fd,
     166           0 :                            int close_flag ) {
     167           0 :   BIO_METHOD * method = fd_bio_nosigpipe_method();
     168           0 :   if( FD_UNLIKELY( !method ) ) return NULL;
     169             : 
     170           0 :   BIO * bio = BIO_new( method );
     171           0 :   if( FD_UNLIKELY( !bio ) ) return NULL;
     172           0 :   BIO_set_fd( bio, fd, close_flag );
     173           0 :   return bio;
     174           0 : }

Generated by: LCOV version 1.14