LCOV - code coverage report
Current view: top level - eventdispatcher - tcp_bio_server.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 163 0.6 %
Date: 2022-06-18 10:10:36 Functions: 2 14 14.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/eventdispatcher
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : /** \file
      21             :  * \brief Event dispatch class.
      22             :  *
      23             :  * Class used to handle events.
      24             :  */
      25             : 
      26             : // make sure we use OpenSSL with multi-thread support
      27             : // (TODO: move to .cpp once we have the impl!)
      28             : #define OPENSSL_THREAD_DEFINES
      29             : 
      30             : #define USE_KLUDGE 1
      31             : 
      32             : // self
      33             : //
      34             : #include    "eventdispatcher/tcp_bio_server.h"
      35             : 
      36             : #include    "eventdispatcher/exception.h"
      37             : #include    "eventdispatcher/tcp_private.h"
      38             : 
      39             : 
      40             : // snaplogger
      41             : //
      42             : #include    <snaplogger/message.h>
      43             : 
      44             : 
      45             : // snapdev
      46             : //
      47             : #include    <snapdev/not_used.h>
      48             : 
      49             : 
      50             : // C++
      51             : //
      52             : #include    <algorithm>
      53             : 
      54             : 
      55             : // OpenSSL
      56             : //
      57             : #include    <openssl/ssl.h>
      58             : 
      59             : 
      60             : // C
      61             : //
      62             : #include    <fcntl.h>
      63             : 
      64             : 
      65             : // last include
      66             : //
      67             : #include    <snapdev/poison.h>
      68             : 
      69             : 
      70             : 
      71             : 
      72             : #ifndef OPENSSL_THREADS
      73             : #error "OPENSSL_THREADS is not defined. Snap! requires support for multiple threads in OpenSSL."
      74             : #endif
      75             : 
      76             : namespace ed
      77             : {
      78             : 
      79             : 
      80             : namespace detail
      81             : {
      82             : 
      83             : 
      84           0 : class tcp_bio_server_impl
      85             : {
      86             : public:
      87             :     int                         f_max_connections = MAX_CONNECTIONS;
      88             :     std::shared_ptr<SSL_CTX>    f_ssl_ctx = std::shared_ptr<SSL_CTX>();
      89             :     std::shared_ptr<BIO>        f_listen = std::shared_ptr<BIO>();
      90             :     bool                        f_keepalive = true;
      91             :     bool                        f_close_on_exec = false;
      92             : };
      93             : 
      94             : 
      95             : }
      96             : 
      97             : 
      98             : /** \class tcp_bio_server
      99             :  * \brief Create a BIO server, bind it, and listen for connections.
     100             :  *
     101             :  * This class is a server socket implementation used to listen for
     102             :  * connections that are to use TLS encryptions.
     103             :  *
     104             :  * The bind address must be available for the server initialization
     105             :  * to succeed.
     106             :  *
     107             :  * The BIO extension is from the OpenSSL library and it allows the server
     108             :  * to allow connections using SSL (TLS really now a day). The server
     109             :  * expects to be given information about a certificate and a private
     110             :  * key to function. You may also use the server in a non-secure manner
     111             :  * (without the TLS layer) so you do not need to implement two instances
     112             :  * of your server, one with tcp_bio_server and one with tcp_server.
     113             :  */
     114             : 
     115             : 
     116             : 
     117             : 
     118             : /** \brief Construct a tcp_bio_server object.
     119             :  *
     120             :  * The tcp_bio_server constructor initializes a BIO server and listens
     121             :  * for connections from the specified address and port.
     122             :  *
     123             :  * The \p certificate and \p private_key filenames are expected to point
     124             :  * to a PEM file (.pem extension) that include the encryption information.
     125             :  *
     126             :  * The certificate file may include a chain in which case the whole chain
     127             :  * will be taken in account.
     128             :  *
     129             :  * \warning
     130             :  * Currently the max_connections parameter is pretty much ignored since
     131             :  * there is no way to pass that parameter down to the BIO interface. In
     132             :  * that code they use the SOMAXCONN definition which under Linux is
     133             :  * defined at 128 (Ubuntu 16.04.1). See:
     134             :  * /usr/include/x86_64-linux-gnu/bits/socket.h
     135             :  *
     136             :  * \param[in] address  The address and port defined in an addr object.
     137             :  * \param[in] max_connections  The number of connections to keep in the listen queue.
     138             :  * \param[in] reuse_addr  Whether to mark the socket with the SO_REUSEADDR flag.
     139             :  * \param[in] certificate  The server certificate filename (PEM).
     140             :  * \param[in] private_key  The server private key filename (PEM).
     141             :  * \param[in] mode  The mode used to create the listening socket.
     142             :  */
     143           0 : tcp_bio_server::tcp_bio_server(
     144             :           addr::addr const & address
     145             :         , int max_connections
     146             :         , bool reuse_addr
     147             :         , std::string const & certificate
     148             :         , std::string const & private_key
     149           0 :         , mode_t mode)
     150           0 :     : f_impl(std::make_shared<detail::tcp_bio_server_impl>())
     151             : {
     152           0 :     f_impl->f_max_connections = std::clamp(max_connections <= 0 ? MAX_CONNECTIONS : max_connections, 5, 1000);
     153             : 
     154           0 :     detail::bio_initialize();
     155             : 
     156           0 :     switch(mode)
     157             :     {
     158           0 :     case mode_t::MODE_ALWAYS_SECURE:
     159             :     case mode_t::MODE_SECURE:
     160             :         {
     161             :             // the following code is based on the example shown in the man page
     162             :             //
     163             :             //        man BIO_f_ssl
     164             :             //
     165           0 :             if(certificate.empty()
     166           0 :             || private_key.empty())
     167             :             {
     168           0 :                 throw initialization_error("with MODE_SECURE you must specify a certificate and a private_key filename");
     169             :             }
     170             : 
     171           0 :             std::shared_ptr<SSL_CTX> ssl_ctx; // use reset(), see SNAP-507
     172           0 :             ssl_ctx.reset(SSL_CTX_new(SSLv23_server_method()), detail::ssl_ctx_deleter);
     173           0 :             if(!ssl_ctx)
     174             :             {
     175           0 :                 detail::bio_log_errors();
     176           0 :                 throw initialization_error("failed creating an SSL_CTX server object");
     177             :             }
     178             : 
     179           0 :             SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL");//"HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
     180             : 
     181             :             // Assign the certificate to the SSL context
     182             :             //
     183             :             // TBD: we may want to use SSL_CTX_use_certificate_file() instead
     184             :             //      (i.e. not the "chained" version)
     185             :             //
     186           0 :             if(!SSL_CTX_use_certificate_chain_file(ssl_ctx.get(), certificate.c_str()))
     187             :             {
     188           0 :                 detail::bio_log_errors();
     189           0 :                 throw initialization_error("failed initializing an SSL_CTX server object certificate");
     190             :             }
     191             : 
     192             :             // Assign the private key to the SSL context
     193             :             //
     194           0 :             if(!SSL_CTX_use_PrivateKey_file(ssl_ctx.get(), private_key.c_str(), SSL_FILETYPE_PEM))
     195             :             {
     196             :                 // on failure, try again again with the RSA version, just in case
     197             :                 // (probably useless?)
     198             :                 //
     199             : #if OPENSSL_VERSION_NUMBER < 0x30000020L
     200           0 :                 if(!SSL_CTX_use_RSAPrivateKey_file(ssl_ctx.get(), private_key.c_str(), SSL_FILETYPE_PEM))
     201             :                 {
     202             : #endif
     203           0 :                     detail::bio_log_errors();
     204           0 :                     throw initialization_error("failed initializing an SSL_CTX server object private key");
     205             : #if OPENSSL_VERSION_NUMBER < 0x30000020L
     206             :                 }
     207             : #endif
     208             :             }
     209             : 
     210             :             // Verify that the private key and certificate are a match
     211             :             //
     212           0 :             if(!SSL_CTX_check_private_key(ssl_ctx.get()))
     213             :             {
     214           0 :                 detail::bio_log_errors();
     215           0 :                 throw initialization_error("failed initializing an SSL_CTX server object private key");
     216             :             }
     217             : 
     218             :             // create a BIO connection with SSL
     219             :             //
     220           0 :             std::unique_ptr<BIO, void (*)(BIO *)> bio(BIO_new_ssl(ssl_ctx.get(), 0), detail::bio_deleter);
     221           0 :             if(bio == nullptr)
     222             :             {
     223           0 :                 detail::bio_log_errors();
     224           0 :                 throw initialization_error("failed initializing a BIO server object");
     225             :             }
     226             : 
     227             :             // get the SSL pointer, which generally means that the BIO
     228             :             // allocate succeeded fully, so we can set auto-retry
     229             :             //
     230           0 :             SSL * ssl(nullptr);
     231             : #pragma GCC diagnostic push
     232             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     233           0 :             BIO_get_ssl(bio.get(), &ssl);
     234             : #pragma GCC diagnostic pop
     235           0 :             if(ssl == nullptr)
     236             :             {
     237             :                 // TBD: does this mean we would have a plain connection?
     238           0 :                 detail::bio_log_errors();
     239           0 :                 throw initialization_error("failed connecting BIO object with SSL_CTX object");
     240             :             }
     241             : 
     242             :             // allow automatic retries in case the connection somehow needs
     243             :             // an SSL renegotiation (maybe we should turn that off for cases
     244             :             // where we connect to a secure payment gateway?)
     245             :             //
     246           0 :             SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
     247             : 
     248             :             // create a listening connection
     249             :             //
     250           0 :             std::shared_ptr<BIO> listen;  // use reset(), see SNAP-507
     251           0 :             listen.reset(BIO_new_accept(address.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_PORT).c_str()), detail::bio_deleter);
     252           0 :             if(!listen)
     253             :             {
     254           0 :                 detail::bio_log_errors();
     255           0 :                 throw initialization_error("failed initializing a BIO server object");
     256             :             }
     257             : 
     258           0 :             BIO_set_bind_mode(listen.get(), reuse_addr ? BIO_BIND_REUSEADDR : BIO_BIND_NORMAL);
     259             : 
     260             :             // Attach the SSL bio to the listening BIO, this means whenever
     261             :             // a new connection is accepted, it automatically attaches it to
     262             :             // an SSL connection
     263             :             //
     264             : #pragma GCC diagnostic push
     265             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     266           0 :             BIO_set_accept_bios(listen.get(), bio.get());
     267             : #pragma GCC diagnostic pop
     268             : 
     269             :             // WARNING: the listen object takes ownership of the `bio`
     270             :             //          pointer and thus we have to make sure that we
     271             :             //          do not keep it in our unique_ptr<>().
     272             :             //
     273           0 :             snapdev::NOT_USED(bio.release());
     274             : 
     275             :             // Actually call bind() and listen() on the socket
     276             :             //
     277             :             // I called BIO_do_accept() before, but this looks cleaner
     278             :             // (although both calls do the same thing)
     279             :             //
     280           0 :             int const r(BIO_do_connect(listen.get()));
     281           0 :             if(r <= 0)
     282             :             {
     283           0 :                 detail::bio_log_errors();
     284           0 :                 throw initialization_error("failed initializing the BIO server socket to listen for client connections");
     285             :             }
     286             : 
     287             : #if USE_KLUDGE
     288             :             // HUGE KLUDGE!
     289             :             //
     290             :             // the BIO_do_connect() does not correctly report errors in
     291             :             // case the IP address is not valid for the bind() call
     292             :             // (i.e. the address is 1.2.3.4 and none of your interfaces
     293             :             // support that IP address) our KLUDGE checks that the socket
     294             :             // is valid because at least the library closes it on failure
     295             :             //
     296             :             // newer versions of the library have a fix since Thu Apr 9 12:36:37 2020 +0100
     297             :             // (it should be in there for 1.1.2 or so)
     298             :             //
     299           0 :             int c(-1);
     300             : #pragma GCC diagnostic push
     301             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     302           0 :             BIO_get_fd(listen.get(), &c);
     303             : #pragma GCC diagnostic pop
     304           0 :             if(c < 0)
     305             :             {
     306           0 :                 std::stringstream ss;
     307             :                 ss << "bind() failed to connect to "
     308           0 :                    << address;
     309           0 :                 throw initialization_error(ss.str());
     310             :             }
     311           0 :             int error_code(0);
     312           0 :             socklen_t error_code_size = sizeof(error_code);
     313           0 :             int const g(getsockopt(c, SOL_SOCKET, SO_ERROR, reinterpret_cast<void *>(&error_code), &error_code_size));
     314           0 :             if(g != 0)
     315             :             {
     316           0 :                 error_code = errno;
     317             :             }
     318           0 :             if(error_code != 0)
     319             :             {
     320           0 :                 std::stringstream ss;
     321             :                 ss << "bind() failed to connect to "
     322             :                    << address
     323           0 :                    << " and reported error #"
     324             :                    << error_code
     325             :                    << ", "
     326           0 :                    << strerror(error_code);
     327           0 :                 throw initialization_error(ss.str());
     328             :             }
     329             : #endif
     330             : 
     331             :             // it worked, save the results
     332           0 :             f_impl->f_ssl_ctx.swap(ssl_ctx);
     333           0 :             f_impl->f_listen.swap(listen);
     334             : 
     335             :             // secure connection ready
     336             :         }
     337           0 :         break;
     338             : 
     339           0 :     case mode_t::MODE_PLAIN:
     340             :         {
     341           0 :             std::shared_ptr<BIO> listen; // use reset(), see SNAP-507
     342           0 :             listen.reset(BIO_new_accept(address.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_PORT).c_str()), detail::bio_deleter);
     343           0 :             if(listen == nullptr)
     344             :             {
     345           0 :                 detail::bio_log_errors();
     346           0 :                 throw initialization_error("failed initializing a BIO server object");
     347             :             }
     348             : 
     349           0 :             BIO_set_bind_mode(listen.get(), BIO_BIND_REUSEADDR);
     350             : 
     351             :             // Call bind() and listen() on the socket
     352             :             //
     353             :             // I called BIO_do_accept() before, but this looks cleaner
     354             :             // (although both calls do the same thing)
     355             :             //
     356           0 :             int const r(BIO_do_connect(listen.get()));
     357           0 :             if(r <= 0)
     358             :             {
     359           0 :                 detail::bio_log_errors();
     360           0 :                 throw initialization_error("failed initializing the BIO server socket to listen for client connections");
     361             :             }
     362             : 
     363             : #if USE_KLUDGE
     364             :             // HUGE KLUDGE!
     365             :             //
     366             :             // the BIO_do_connect() does not correctly report errors in
     367             :             // case the IP address is not valid for the bind() call
     368             :             // (i.e. the address is 1.2.3.4 and none of your interfaces
     369             :             // support that IP address) our KLUDGE checks that the socket
     370             :             // is valid because at least the library closes it on failure
     371             :             //
     372             :             // newer versions of the library have a fix since Thu Apr 9 12:36:37 2020 +0100
     373             :             // (it should be in there for 1.1.2 or so)
     374             :             //
     375           0 :             int c(-1);
     376             : #pragma GCC diagnostic push
     377             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     378           0 :             BIO_get_fd(listen.get(), &c);
     379             : #pragma GCC diagnostic pop
     380           0 :             if(c < 0)
     381             :             {
     382           0 :                 std::stringstream ss;
     383             :                 ss << "bind() failed to connect to "
     384           0 :                    << address;
     385           0 :                 throw initialization_error(ss.str());
     386             :             }
     387           0 :             int error_code(ENOTCONN);
     388           0 :             socklen_t error_code_size = sizeof(error_code);
     389           0 :             int const g(getsockopt(c, SOL_SOCKET, SO_ERROR, reinterpret_cast<void *>(&error_code), &error_code_size));
     390           0 :             if(g != 0)
     391             :             {
     392           0 :                 error_code = errno;
     393             :             }
     394           0 :             if(error_code != 0)
     395             :             {
     396           0 :                 std::stringstream ss;
     397             :                 ss << "bind() failed to connect to "
     398             :                    << address
     399           0 :                    << " and reported error #"
     400             :                    << error_code
     401             :                    << ", "
     402           0 :                    << strerror(error_code);
     403           0 :                 throw initialization_error(ss.str());
     404             :             }
     405             : #endif
     406             : 
     407             :             // it worked, save the results
     408             :             //
     409           0 :             f_impl->f_listen.swap(listen);
     410             :         }
     411           0 :         break;
     412             : 
     413             :     }
     414           0 : }
     415             : 
     416             : 
     417             : /** \brief Clean up the TCP Bio Server object.
     418             :  *
     419             :  * This function cleans up the object on destruction.
     420             :  */
     421           0 : tcp_bio_server::~tcp_bio_server()
     422             : {
     423           0 : }
     424             : 
     425             : 
     426             : /** \brief Return the current status of the keepalive flag.
     427             :  *
     428             :  * This function returns the current status of the keepalive flag. This
     429             :  * flag is set to true by default (in the constructor.) It can be
     430             :  * changed with the set_keepalive() function.
     431             :  *
     432             :  * The flag is used to mark new connections with the SO_KEEPALIVE flag.
     433             :  * This is used whenever a service may take a little to long to answer
     434             :  * and avoid losing the TCP connection before the answer is sent to
     435             :  * the client.
     436             :  *
     437             :  * \warning
     438             :  * It is very likely that the BIO interface forces the keepalive flag
     439             :  * automatically so even if false here, it is very likely that your
     440             :  * connection will either way be marked as keepalive.
     441             :  *
     442             :  * \return The current status of the keepalive flag.
     443             :  */
     444           0 : bool tcp_bio_server::get_keepalive() const
     445             : {
     446           0 :     return f_impl->f_keepalive;
     447             : }
     448             : 
     449             : 
     450             : /** \brief Set the keepalive flag.
     451             :  *
     452             :  * This function sets the keepalive flag to either true (i.e. mark connection
     453             :  * sockets with the SO_KEEPALIVE flag) or false. The default is true (as set
     454             :  * in the constructor,) because in most cases this is a feature people want.
     455             :  *
     456             :  * \warning
     457             :  * The keepalive flag is likely force within the BIO interface, so setting it
     458             :  * to true here (which is the default anyway) probably has no real effect
     459             :  * (i.e. the fact is set a second time).
     460             :  *
     461             :  * \param[in] yes  Whether to keep new connections alive even when no traffic
     462             :  * goes through.
     463             :  */
     464           0 : void tcp_bio_server::set_keepalive(bool yes)
     465             : {
     466           0 :     f_impl->f_keepalive = yes;
     467           0 : }
     468             : 
     469             : 
     470             : /** \brief Return the current status of the close_on_exec flag.
     471             :  *
     472             :  * This function returns the current status of the close_on_exec flag. This
     473             :  * flag is set to false by default (in the constructor.) It can be
     474             :  * changed with the set_close_on_exec() function.
     475             :  *
     476             :  * The flag is used to atomically mark new connections with the FD_CLOEXEC
     477             :  * flag. This prevents child processes from inheriting the socket (i.e. if
     478             :  * you use the system() function, for example, that process would inherit
     479             :  * your socket).
     480             :  *
     481             :  * \return The current status of the close_on_exec flag.
     482             :  */
     483           0 : bool tcp_bio_server::get_close_on_exec() const
     484             : {
     485           0 :     return f_impl->f_close_on_exec;
     486             : }
     487             : 
     488             : 
     489             : /** \brief Set the close_on_exec flag.
     490             :  *
     491             :  * This function sets the close_on_exec flag to either true (i.e. mark connection
     492             :  * sockets with the FD_CLOEXEC flag) or false. The default is false (as set
     493             :  * in the constructor,) because in our legacy code, the flag is not expected
     494             :  * to be set.
     495             :  *
     496             :  * \note
     497             :  * When set to true, the FD_CLOEXEC is also set on the listening socket so
     498             :  * the child can't snatch connections from under our feet.
     499             :  *
     500             :  * \warning
     501             :  * This is not thread safe. The BIO_do_accept() implementation uses the
     502             :  * accept() function which then returns and we set the FD_CLOEXEC flag
     503             :  * on the socket. This means it's not secure if you use exec() in a
     504             :  * separate thread (i.e. it may share the socket anyway unless your accept
     505             :  * is protected from such things). If you need to have a separate process,
     506             :  * look into using a fork() instead of force close the sockets in the
     507             :  * child process.
     508             :  *
     509             :  * \param[in] yes  Whether to close on exec() or not.
     510             :  */
     511           0 : void tcp_bio_server::set_close_on_exec(bool yes)
     512             : {
     513           0 :     f_impl->f_close_on_exec = yes;
     514             : 
     515           0 :     if(yes)
     516             :     {
     517             :         // retrieve the socket (we do not yet have a bio_client object
     518             :         // so we cannot call a get_socket() function...)
     519             :         //
     520           0 :         int socket(-1);
     521             : #pragma GCC diagnostic push
     522             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     523           0 :         BIO_get_fd(f_impl->f_listen.get(), &socket);
     524             : #pragma GCC diagnostic pop
     525           0 :         if(socket >= 0)
     526             :         {
     527             :             // if this call fails, we ignore the error, but still log the event
     528             :             //
     529           0 :             if(fcntl(socket, F_SETFD, FD_CLOEXEC) != 0)
     530             :             {
     531           0 :                 int const e(errno);
     532           0 :                 SNAP_LOG_WARNING
     533             :                     << "tcp_bio_server::set_close_on_exec(): an error occurred trying"
     534           0 :                        " to mark accepted socket with FD_CLOEXEC ("
     535             :                     << e
     536             :                     << ", "
     537           0 :                     << strerror(e)
     538             :                     << ")."
     539             :                     << SNAP_LOG_SEND;
     540             :             }
     541             :         }
     542             :     }
     543           0 : }
     544             : 
     545             : 
     546             : /** \brief Tell you whether the server uses a secure BIO or not.
     547             :  *
     548             :  * This function checks whether the BIO is using encryption (true)
     549             :  * or is a plain connection (false).
     550             :  *
     551             :  * \return true if the BIO was created in secure mode.
     552             :  */
     553           0 : bool tcp_bio_server::is_secure() const
     554             : {
     555           0 :     return f_impl->f_ssl_ctx != nullptr;
     556             : }
     557             : 
     558             : 
     559             : /** \brief Get the listening socket.
     560             :  *
     561             :  * This function returns the file descriptor of the listening socket.
     562             :  * By default the socket is in blocking mode.
     563             :  *
     564             :  * \return The listening socket file descriptor.
     565             :  */
     566           0 : int tcp_bio_server::get_socket() const
     567             : {
     568           0 :     if(f_impl->f_listen)
     569             :     {
     570           0 :         int c(-1);
     571             : #pragma GCC diagnostic push
     572             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     573           0 :         BIO_get_fd(f_impl->f_listen.get(), &c);
     574             : #pragma GCC diagnostic pop
     575           0 :         return c;
     576             :     }
     577             : 
     578           0 :     return -1;
     579             : }
     580             : 
     581             : 
     582             : /** \brief Retrieve one new connection.
     583             :  *
     584             :  * This function will wait until a new connection arrives and returns a
     585             :  * new bio_client object for each new connection.
     586             :  *
     587             :  * If the socket is made non-blocking then the function may return without
     588             :  * a bio_client object (i.e. a null pointer instead.)
     589             :  *
     590             :  * \return A shared pointer to a newly allocated bio_client object.
     591             :  */
     592           0 : tcp_bio_client::pointer_t tcp_bio_server::accept()
     593             : {
     594             :     // TBD: does one call to BIO_do_accept() accept at most one connection
     595             :     //      at a time or could it be that 'r' will be set to 2, 3, 4...
     596             :     //      as more connections get accepted?
     597             :     //
     598           0 :     int const r(BIO_do_accept(f_impl->f_listen.get()));
     599           0 :     if(r <= 0)
     600             :     {
     601             :         // TBD: should we instead return an empty shared pointer in this case?
     602             :         //
     603           0 :         detail::bio_log_errors();
     604           0 :         throw runtime_error("failed accepting a new BIO client");
     605             :     }
     606             : 
     607             :     // retrieve the new connection by "popping it"
     608             :     //
     609           0 :     std::shared_ptr<BIO> bio; // use reset(), see SNAP-507
     610           0 :     bio.reset(BIO_pop(f_impl->f_listen.get()), detail::bio_deleter);
     611           0 :     if(bio == nullptr)
     612             :     {
     613           0 :         detail::bio_log_errors();
     614           0 :         throw runtime_error("failed retrieving the accepted BIO");
     615             :     }
     616             : 
     617             :     // mark the new connection with the SO_KEEPALIVE flag
     618             :     //
     619             :     // note that it is likely that the BIO implementation already does that
     620             :     // automatically once the accept() succeeded.
     621             :     //
     622           0 :     if(f_impl->f_keepalive)
     623             :     {
     624             :         // retrieve the socket (we do not yet have a bio_client object
     625             :         // so we cannot call a get_socket() function...)
     626             :         //
     627           0 :         int socket(-1);
     628             : #pragma GCC diagnostic push
     629             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     630           0 :         BIO_get_fd(bio.get(), &socket);
     631             : #pragma GCC diagnostic pop
     632           0 :         if(socket >= 0)
     633             :         {
     634             :             // if this call fails, we ignore the error, but still log the event
     635             :             //
     636           0 :             int optval(1);
     637           0 :             socklen_t const optlen(sizeof(optval));
     638           0 :             if(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) != 0)
     639             :             {
     640           0 :                 SNAP_LOG_WARNING
     641             :                     << "tcp_bio_server::accept(): an error occurred trying"
     642             :                        " to mark accepted socket with SO_KEEPALIVE."
     643             :                     << SNAP_LOG_SEND;
     644             :             }
     645             :         }
     646             :     }
     647             : 
     648             :     // force a close on exec() to avoid sharing the socket in child processes
     649           0 :     if(f_impl->f_close_on_exec)
     650             :     {
     651             :         // retrieve the socket (we do not yet have a bio_client object
     652             :         // so we cannot call a get_socket() function...)
     653             :         //
     654           0 :         int socket(-1);
     655             : #pragma GCC diagnostic push
     656             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     657           0 :         BIO_get_fd(bio.get(), &socket);
     658             : #pragma GCC diagnostic pop
     659           0 :         if(socket >= 0)
     660             :         {
     661             :             // if this call fails, we ignore the error, but still log the event
     662             :             //
     663           0 :             if(fcntl(socket, F_SETFD, FD_CLOEXEC) != 0)
     664             :             {
     665           0 :                 SNAP_LOG_WARNING
     666             :                     << "tcp_bio_server::accept(): an error occurred trying"
     667             :                        " to mark accepted socket with FD_CLOEXEC."
     668             :                     << SNAP_LOG_SEND;
     669             :             }
     670             :         }
     671             :     }
     672             : 
     673             :     // tcp_bio_client is private so we can't use the std::make_shared<>
     674             :     //
     675           0 :     tcp_bio_client::pointer_t client(new tcp_bio_client);
     676             : 
     677           0 :     client->f_impl->f_bio = bio;
     678             : 
     679             :     // TODO: somehow this does not seem to give us any information
     680             :     //       about the cipher and other details...
     681             :     //
     682             :     //       this is because it is (way) too early, we did not even
     683             :     //       receive the HELLO yet!
     684             :     //
     685           0 :     SSL * ssl(nullptr);
     686             : #pragma GCC diagnostic push
     687             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     688           0 :     BIO_get_ssl(bio.get(), &ssl);
     689             : #pragma GCC diagnostic pop
     690           0 :     if(ssl != nullptr)
     691             :     {
     692           0 :         char const * cipher_name(SSL_get_cipher(ssl));
     693           0 :         int cipher_bits(0);
     694           0 :         SSL_get_cipher_bits(ssl, &cipher_bits);
     695           0 :         SNAP_LOG_DEBUG
     696             :             << "accepted BIO client with SSL cipher \""
     697             :             << cipher_name
     698           0 :             << "\" representing "
     699             :             << cipher_bits
     700             :             << " bits of encryption."
     701             :             << SNAP_LOG_SEND;
     702             :     }
     703             : 
     704           0 :     return client;
     705             : }
     706             : 
     707             : 
     708             : 
     709           6 : } // namespace ed
     710             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13