LCOV - code coverage report
Current view: top level - eventdispatcher - tcp_bio_client.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 167 0.6 %
Date: 2022-06-18 10:10:36 Functions: 2 13 15.4 %
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
      17             : // along with this program; if not, write to the Free Software
      18             : // Foundation, Inc., 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             : //
      28             : #define OPENSSL_THREAD_DEFINES
      29             : 
      30             : // self
      31             : //
      32             : #include    "eventdispatcher/tcp_bio_client.h"
      33             : 
      34             : #include    "eventdispatcher/exception.h"
      35             : #include    "eventdispatcher/tcp_private.h"
      36             : 
      37             : 
      38             : // snaplogger lib
      39             : //
      40             : #include    <snaplogger/message.h>
      41             : 
      42             : 
      43             : // snapdev lib
      44             : //
      45             : #include    <snapdev/not_reached.h>
      46             : 
      47             : 
      48             : // OpenSSL lib
      49             : //
      50             : #include    <openssl/bio.h>
      51             : #include    <openssl/err.h>
      52             : #include    <openssl/ssl.h>
      53             : 
      54             : 
      55             : // C lib
      56             : //
      57             : #include    <netdb.h>
      58             : #include    <arpa/inet.h>
      59             : 
      60             : 
      61             : // last include
      62             : //
      63             : #include    <snapdev/poison.h>
      64             : 
      65             : 
      66             : 
      67             : 
      68             : #ifndef OPENSSL_THREADS
      69             : #error "OPENSSL_THREADS is not defined. Snap! requires support for multiple threads in OpenSSL."
      70             : #endif
      71             : 
      72             : namespace ed
      73             : {
      74             : 
      75             : 
      76             : 
      77             : /** \class tcp_bio_client
      78             :  * \brief Create a BIO client and connect to a server, eventually with TLS.
      79             :  *
      80             :  * This class is a client socket implementation used to connect to a server.
      81             :  * The server is expected to be running at the time the client is created
      82             :  * otherwise it fails connecting.
      83             :  *
      84             :  * This class is not appropriate to connect to a server that may come and go
      85             :  * over time.
      86             :  *
      87             :  * The BIO extension is from the OpenSSL library and it allows the client
      88             :  * to connect using SSL. At this time connections are either secure or
      89             :  * not secure. If a secure connection fails, you may attempt again without
      90             :  * TLS or other encryption mechanism.
      91             :  */
      92             : 
      93             : 
      94             : #if 0
      95             : // code to do traces
      96             : namespace
      97             : {
      98             : 
      99             : 
     100             : char const * tls_rt_type(int type)
     101             : {
     102             :     switch(type)
     103             :     {
     104             : #ifdef SSL3_RT_HEADER
     105             :     case SSL3_RT_HEADER:
     106             :         return "TLS header";
     107             : #endif
     108             :     case SSL3_RT_CHANGE_CIPHER_SPEC:
     109             :         return "TLS change cipher";
     110             :     case SSL3_RT_ALERT:
     111             :         return "TLS alert";
     112             :     case SSL3_RT_HANDSHAKE:
     113             :         return "TLS handshake";
     114             :     case SSL3_RT_APPLICATION_DATA:
     115             :         return "TLS app data";
     116             :     default:
     117             :         return "TLS Unknown";
     118             :     }
     119             : }
     120             : 
     121             : 
     122             : char const * ssl_msg_type(int ssl_ver, int msg)
     123             : {
     124             : #ifdef SSL2_VERSION_MAJOR
     125             :     if(ssl_ver == SSL2_VERSION_MAJOR)
     126             :     {
     127             :         switch(msg)
     128             :         {
     129             :         case SSL2_MT_ERROR:
     130             :             return "Error";
     131             :         case SSL2_MT_CLIENT_HELLO:
     132             :             return "Client hello";
     133             :         case SSL2_MT_CLIENT_MASTER_KEY:
     134             :             return "Client key";
     135             :         case SSL2_MT_CLIENT_FINISHED:
     136             :             return "Client finished";
     137             :         case SSL2_MT_SERVER_HELLO:
     138             :             return "Server hello";
     139             :         case SSL2_MT_SERVER_VERIFY:
     140             :             return "Server verify";
     141             :         case SSL2_MT_SERVER_FINISHED:
     142             :             return "Server finished";
     143             :         case SSL2_MT_REQUEST_CERTIFICATE:
     144             :             return "Request CERT";
     145             :         case SSL2_MT_CLIENT_CERTIFICATE:
     146             :             return "Client CERT";
     147             :         }
     148             :     }
     149             :     else
     150             : #endif
     151             :     if(ssl_ver == SSL3_VERSION_MAJOR)
     152             :     {
     153             :         switch(msg)
     154             :         {
     155             :         case SSL3_MT_HELLO_REQUEST:
     156             :             return "Hello request";
     157             :         case SSL3_MT_CLIENT_HELLO:
     158             :             return "Client hello";
     159             :         case SSL3_MT_SERVER_HELLO:
     160             :             return "Server hello";
     161             : #ifdef SSL3_MT_NEWSESSION_TICKET
     162             :         case SSL3_MT_NEWSESSION_TICKET:
     163             :             return "Newsession Ticket";
     164             : #endif
     165             :         case SSL3_MT_CERTIFICATE:
     166             :             return "Certificate";
     167             :         case SSL3_MT_SERVER_KEY_EXCHANGE:
     168             :             return "Server key exchange";
     169             :         case SSL3_MT_CLIENT_KEY_EXCHANGE:
     170             :             return "Client key exchange";
     171             :         case SSL3_MT_CERTIFICATE_REQUEST:
     172             :             return "Request CERT";
     173             :         case SSL3_MT_SERVER_DONE:
     174             :             return "Server finished";
     175             :         case SSL3_MT_CERTIFICATE_VERIFY:
     176             :             return "CERT verify";
     177             :         case SSL3_MT_FINISHED:
     178             :             return "Finished";
     179             : #ifdef SSL3_MT_CERTIFICATE_STATUS
     180             :         case SSL3_MT_CERTIFICATE_STATUS:
     181             :             return "Certificate Status";
     182             : #endif
     183             :         }
     184             :     }
     185             :     return "Unknown";
     186             : }
     187             : 
     188             : 
     189             : void ssl_trace(
     190             :         int direction,
     191             :         int ssl_ver,
     192             :         int content_type,
     193             :         void const * buf, size_t len,
     194             :         SSL * ssl,
     195             :         void * userp)
     196             : {
     197             :     snapdev::NOT_USED(ssl, userp);
     198             : 
     199             :     std::stringstream out;
     200             :     char const * msg_name;
     201             :     int msg_type;
     202             : 
     203             :     // VERSION
     204             :     //
     205             :     out << SSL_get_version(ssl);
     206             : 
     207             :     // DIRECTION
     208             :     //
     209             :     out << (direction == 0 ? " (IN), " : " (OUT), ");
     210             : 
     211             :     // keep only major version
     212             :     //
     213             :     ssl_ver >>= 8;
     214             : 
     215             :     // TLS RT NAME
     216             :     //
     217             :     if(ssl_ver == SSL3_VERSION_MAJOR
     218             :     && content_type != 0)
     219             :     {
     220             :         out << tls_rt_type(content_type);
     221             :     }
     222             :     else
     223             :     {
     224             :         out << "(no tls_tr_type)";
     225             :     }
     226             : 
     227             :     if(len >= 1)
     228             :     {
     229             :         msg_type = * reinterpret_cast<unsigned char const *>(buf);
     230             :         msg_name = ssl_msg_type(ssl_ver, msg_type);
     231             : 
     232             :         out << ", ";
     233             :         out << msg_name;
     234             :         out << " (";
     235             :         out << std::to_string(msg_type);
     236             :         out << "):";
     237             :     }
     238             : 
     239             :     out << std::hex;
     240             :     for(size_t line(0); line < len; line += 16)
     241             :     {
     242             :         out << std::endl
     243             :             << (direction == 0 ? "<" : ">")
     244             :             << " "
     245             :             << std::setfill('0') << std::setw(4) << line
     246             :             << "-  ";
     247             :         size_t idx;
     248             :         for(idx = 0; line + idx < len && idx < 16; ++idx)
     249             :         {
     250             :             if(idx == 8)
     251             :             {
     252             :                 out << "   ";
     253             :             }
     254             :             else
     255             :             {
     256             :                 out << " ";
     257             :             }
     258             :             int const c(reinterpret_cast<unsigned char const *>(buf)[line + idx]);
     259             :             out << std::setfill('0') << std::setw(2) << static_cast<int>(c);
     260             :         }
     261             :         for(; idx < 16; ++idx)
     262             :         {
     263             :             if(idx == 8)
     264             :             {
     265             :                 out << "  ";
     266             :             }
     267             :             out << "   ";
     268             :         }
     269             :         out << "   ";
     270             :         for(idx = 0; line + idx < len && idx < 16; ++idx)
     271             :         {
     272             :             if(idx == 8)
     273             :             {
     274             :                 out << " ";
     275             :             }
     276             :             char c(reinterpret_cast<char const *>(buf)[line + idx]);
     277             :             if(c < ' ' || c > '~')
     278             :             {
     279             :                 c = '.';
     280             :             }
     281             :             out << c;
     282             :         }
     283             :     }
     284             : 
     285             :     std::cerr << out.str() << std::endl;
     286             : }
     287             : 
     288             : 
     289             : } // no name namespace
     290             : #endif
     291             : 
     292             : 
     293             : 
     294             : 
     295             : 
     296             : 
     297             : 
     298             : 
     299             : 
     300             : 
     301             : 
     302             : /** \brief Construct a tcp_bio_client object.
     303             :  *
     304             :  * The tcp_bio_client constructor initializes a BIO connector and connects
     305             :  * to the specified server. The server is defined with the \p address
     306             :  * specified as parameter. The connection tries to use TLS if the \p mode
     307             :  * parameter is set to MODE_SECURE. Note that you may force a secure
     308             :  * connection using MODE_SECURE_REQUIRED. With MODE_SECURE,
     309             :  * the connection to the server can be obtained even if a secure
     310             :  * connection was not available.
     311             :  *
     312             :  * \todo
     313             :  * Create another client with BIO_new_socket() so one can create an SSL
     314             :  * connection with a socket retrieved from an accept() call.
     315             :  *
     316             :  * \exception tcp_client_server_parameter_error
     317             :  * This exception is raised if the \p port parameter is out of range or the
     318             :  * IP address is an empty string or otherwise an invalid address.
     319             :  *
     320             :  * \exception tcp_client_server_initialization_error
     321             :  * This exception is raised if the client cannot create the socket or it
     322             :  * cannot connect to the server.
     323             :  *
     324             :  * \param[in] address  The address of the server to connect to. It must be valid.
     325             :  * \param[in] mode  Whether to use SSL when connecting.
     326             :  * \param[in] opt  Additional options.
     327             :  */
     328           0 : tcp_bio_client::tcp_bio_client(
     329             :               addr::addr const & address
     330             :             , mode_t mode
     331           0 :             , tcp_bio_options const & opt)
     332             :     : f_address(address)
     333           0 :     , f_impl(std::make_shared<detail::tcp_bio_client_impl>())
     334             : {
     335           0 :     if(address.is_default())
     336             :     {
     337           0 :         throw invalid_parameter("the default address is not valid for a client socket");
     338             :     }
     339             : 
     340           0 :     detail::bio_initialize();
     341             : 
     342           0 :     switch(mode)
     343             :     {
     344           0 :     case mode_t::MODE_SECURE:
     345             :     case mode_t::MODE_ALWAYS_SECURE:
     346             :         {
     347             :             // Use TLS v1 only as all versions of SSL are flawed...
     348             :             // (see below the SSL_CTX_set_options() for additional details
     349             :             // about that since here it does indeed say SSLv23...)
     350             :             //
     351           0 :             std::shared_ptr<SSL_CTX> ssl_ctx; // use a reset(), see SNAP-507
     352           0 :             ssl_ctx.reset(SSL_CTX_new(SSLv23_client_method()), detail::ssl_ctx_deleter);
     353           0 :             if(ssl_ctx == nullptr)
     354             :             {
     355           0 :                 detail::bio_log_errors();
     356           0 :                 throw initialization_error("failed creating an SSL_CTX object");
     357             :             }
     358             : 
     359             :             // allow up to `depth` certificates in the chain otherwise fail
     360             :             // (this is not a very strong security feature though); the depth
     361             :             // can be changed before calling this function using a
     362             :             // tcp_bio_options object
     363             :             //
     364           0 :             SSL_CTX_set_verify_depth(ssl_ctx.get(), opt.get_verification_depth());
     365             : 
     366             :             // make sure SSL v2/3 is not used, also compression in SSL is
     367             :             // known to have security issues
     368             :             //
     369           0 :             SSL_CTX_set_options(ssl_ctx.get(), opt.get_ssl_options());
     370             : 
     371             :             // limit the number of ciphers the connection can use
     372           0 :             if(mode == mode_t::MODE_SECURE)
     373             :             {
     374             :                 // this is used by local connections and we get a very strong
     375             :                 // algorithm anyway, but at this point I do not know why it
     376             :                 // does not work with the limited list below...
     377             :                 //
     378             :                 // TODO: test with adding DH support in the server then
     379             :                 //       maybe (probably) that the "HIGH" will work for
     380             :                 //       this entry too...
     381             :                 //
     382           0 :                 SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL");
     383             :             }
     384             :             else
     385             :             {
     386           0 :                 SSL_CTX_set_cipher_list(ssl_ctx.get(), "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
     387             :             }
     388             : 
     389             :             // load root certificates (correct path for Ubuntu?)
     390             :             // TODO: allow client to set the path to certificates
     391           0 :             if(SSL_CTX_load_verify_locations(ssl_ctx.get(), nullptr, "/etc/ssl/certs") != 1)
     392             :             {
     393           0 :                 detail::bio_log_errors();
     394           0 :                 throw initialization_error("failed loading verification certificates in an SSL_CTX object");
     395             :             }
     396             :             //SSL_CTX_set_msg_callback(ssl_ctx.get(), ssl_trace);
     397             :             //SSL_CTX_set_msg_callback_arg(ssl_ctx.get(), this);
     398             : 
     399             :             // create a BIO connected to SSL ciphers
     400             :             //
     401           0 :             std::shared_ptr<BIO> bio;
     402           0 :             bio.reset(BIO_new_ssl_connect(ssl_ctx.get()), detail::bio_deleter);
     403           0 :             if(!bio)
     404             :             {
     405           0 :                 detail::bio_log_errors();
     406           0 :                 throw initialization_error("failed initializing a BIO object");
     407             :             }
     408             : 
     409             :             // verify that the connection worked
     410             :             //
     411           0 :             SSL * ssl(nullptr);
     412             : #pragma GCC diagnostic push
     413             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     414           0 :             BIO_get_ssl(bio.get(), &ssl);
     415             : #pragma GCC diagnostic pop
     416           0 :             if(ssl == nullptr)
     417             :             {
     418             :                 // TBD: does this mean we would have a plain connection?
     419           0 :                 detail::bio_log_errors();
     420           0 :                 throw initialization_error("failed retrieving the SSL contact from BIO object");
     421             :             }
     422             : 
     423             :             // allow automatic retries in case the connection somehow needs
     424             :             // an SSL renegotiation (maybe we should turn that off for cases
     425             :             // where we connect to a secure payment gateway?)
     426             :             //
     427           0 :             SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
     428             : 
     429             :             // setup the Server Name Indication (SNI)
     430             :             //
     431           0 :             bool using_sni(false);
     432           0 :             if(opt.get_sni())
     433             :             {
     434           0 :                 std::string host(opt.get_host());
     435           0 :                 if(host.empty()
     436           0 :                 && !address.is_hostname_an_ip())
     437             :                 {
     438             :                     // addr is not an IP address written as is,
     439             :                     // it must be a hostname
     440             :                     //
     441           0 :                     host = address.get_hostname();
     442             :                 }
     443           0 :                 if(!host.empty())
     444             :                 {
     445             : #pragma GCC diagnostic push
     446             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     447             :                     // not only old style cast (but it's C, so expected)
     448             :                     // but they want a non-constant pointer!?
     449             :                     //
     450           0 :                     SSL_set_tlsext_host_name(ssl, const_cast<char *>(host.c_str()));
     451             : #pragma GCC diagnostic pop
     452           0 :                     using_sni = true;
     453             :                 }
     454             :             }
     455             : 
     456             :             // TODO: other SSL initialization?
     457             : 
     458             : #pragma GCC diagnostic push
     459             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     460           0 :             BIO_set_conn_hostname(bio.get(), const_cast<char *>(address.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY).c_str()));
     461           0 :             BIO_set_conn_port(bio.get(), const_cast<char *>(std::to_string(address.get_port()).c_str()));
     462             : #pragma GCC diagnostic pop
     463             : 
     464             :             // connect to the server (open the socket)
     465             :             //
     466           0 :             if(BIO_do_connect(bio.get()) <= 0)
     467             :             {
     468           0 :                 if(!using_sni)
     469             :                 {
     470           0 :                     SNAP_LOG_WARNING
     471             :                         << "the SNI feature is turned off,"
     472             :                            " often failure to connect with SSL is because the"
     473             :                            " SSL Hello message is missing the SNI (Server Name In)."
     474             :                            " See the tcp_bio_options::set_sni()."
     475             :                         << SNAP_LOG_SEND;
     476             :                 }
     477           0 :                 detail::bio_log_errors();
     478           0 :                 throw failed_connecting("SSL BIO_do_connect() failed connecting BIO object to server");
     479             :             }
     480             : 
     481             :             // encryption handshake
     482             :             //
     483           0 :             if(BIO_do_handshake(bio.get()) != 1)
     484             :             {
     485           0 :                 if(!using_sni)
     486             :                 {
     487           0 :                     SNAP_LOG_WARNING
     488             :                         << "the SNI feature is turned off,"
     489             :                            " often failure to connect with SSL is because the"
     490             :                            " SSL Hello message is missing the SNI (Server Name In)."
     491             :                            " See the tcp_bio_options::set_sni()."
     492             :                         << SNAP_LOG_SEND;
     493             :                 }
     494           0 :                 detail::bio_log_errors();
     495             :                 throw initialization_error("failed establishing a secure BIO connection with server, handshake failed."
     496             :                             " Often such failures to process SSL is because the SSL Hello message is missing the SNI (Server Name In)."
     497           0 :                             " See the tcp_bio_options::set_sni().");
     498             :             }
     499             : 
     500             :             // verify that the peer certificate was signed by a
     501             :             // recognized root authority
     502             :             //
     503           0 :             if(SSL_get_peer_certificate(ssl) == nullptr)
     504             :             {
     505           0 :                 detail::bio_log_errors();
     506           0 :                 throw initialization_error("peer failed presenting a certificate for security verification");
     507             :             }
     508             : 
     509             :             // XXX: check that the call below is similar to the example
     510             :             //      usage of SSL_CTX_set_verify() which checks the name
     511             :             //      of the certificate, etc.
     512             :             //
     513           0 :             if(SSL_get_verify_result(ssl) != X509_V_OK)
     514             :             {
     515           0 :                 if(mode != mode_t::MODE_SECURE)
     516             :                 {
     517           0 :                     detail::bio_log_errors();
     518           0 :                     throw initialization_error("peer certificate could not be verified");
     519             :                 }
     520           0 :                 SNAP_LOG_WARNING
     521             :                     << "connecting with SSL but certificate verification failed."
     522             :                     << SNAP_LOG_SEND;
     523             :             }
     524             : 
     525             :             // it worked, save the results
     526             :             //
     527           0 :             f_impl->f_ssl_ctx.swap(ssl_ctx);
     528           0 :             f_impl->f_bio.swap(bio);
     529             : 
     530             :             // secure connection ready
     531             :             //
     532           0 :             char const * cipher_name(SSL_get_cipher(ssl));
     533           0 :             int cipher_bits(0);
     534           0 :             SSL_get_cipher_bits(ssl, &cipher_bits);
     535           0 :             SNAP_LOG_DEBUG
     536             :                 << "connected with SSL cipher \""
     537             :                 << cipher_name
     538           0 :                 << "\" representing "
     539             :                 << cipher_bits
     540             :                 << " bits of encryption."
     541           0 :                 << SNAP_LOG_SEND;
     542             :         }
     543           0 :         break;
     544             : 
     545           0 :     case mode_t::MODE_PLAIN:
     546             :         {
     547             :             // create a plain BIO connection
     548             :             //
     549           0 :             std::shared_ptr<BIO> bio;  // use reset(), see SNAP-507
     550           0 :             bio.reset(BIO_new(BIO_s_connect()), detail::bio_deleter);
     551           0 :             if(!bio)
     552             :             {
     553           0 :                 detail::bio_log_errors();
     554           0 :                 throw initialization_error("failed initializing a BIO object");
     555             :             }
     556             : 
     557             : #pragma GCC diagnostic push
     558             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     559           0 :             BIO_set_conn_hostname(bio.get(), const_cast<char *>(address.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY).c_str()));
     560           0 :             BIO_set_conn_port(bio.get(), const_cast<char *>(std::to_string(address.get_port()).c_str()));
     561             : #pragma GCC diagnostic pop
     562             : 
     563             :             // connect to the server (open the socket)
     564             :             //
     565           0 :             if(BIO_do_connect(bio.get()) <= 0)
     566             :             {
     567           0 :                 detail::bio_log_errors();
     568           0 :                 throw failed_connecting("failed connecting BIO object to server");
     569             :             }
     570             : 
     571             :             // it worked, save the results
     572             :             //
     573           0 :             f_impl->f_bio.swap(bio);
     574             : 
     575             :             // plain connection ready
     576             :         }
     577           0 :         break;
     578             : 
     579             :     }
     580             : 
     581           0 :     if(opt.get_keepalive())
     582             :     {
     583             :         // retrieve the socket (we are still in the constructor so avoid
     584             :         // calling other functions...)
     585             :         //
     586           0 :         int socket(-1);
     587             : #pragma GCC diagnostic push
     588             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     589           0 :         BIO_get_fd(f_impl->f_bio.get(), &socket);
     590             : #pragma GCC diagnostic pop
     591           0 :         if(socket >= 0)
     592             :         {
     593             :             // if this call fails, we ignore the error, but still log the event
     594             :             //
     595           0 :             int optval(1);
     596           0 :             socklen_t const optlen(sizeof(optval));
     597           0 :             if(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) != 0)
     598             :             {
     599           0 :                 SNAP_LOG_WARNING
     600             :                     << "an error occurred trying to mark client socket with SO_KEEPALIVE."
     601             :                     << SNAP_LOG_SEND;
     602             :             }
     603             :         }
     604             :     }
     605           0 : }
     606             : 
     607             : 
     608             : /** \brief Create a BIO client object from an actual BIO pointer.
     609             :  *
     610             :  * This function is called by the server whenever it accepts a new BIO
     611             :  * connection. The server then can return the tcp_bio_client object instead
     612             :  * of a BIO object.
     613             :  *
     614             :  * The BIO is saved directly in the f_impl class (the server is given access.)
     615             :  */
     616           0 : tcp_bio_client::tcp_bio_client()
     617           0 :     : f_impl(std::make_shared<detail::tcp_bio_client_impl>())
     618             : {
     619           0 : }
     620             : 
     621             : 
     622             : /** \brief Clean up the BIO client object.
     623             :  *
     624             :  * This function cleans up the BIO client object by freeing the SSL_CTX
     625             :  * and the BIO objects.
     626             :  */
     627           0 : tcp_bio_client::~tcp_bio_client()
     628             : {
     629             :     // f_bio and f_ssl_ctx are allocated using shared pointers with
     630             :     // a deleter so we have nothing to do here.
     631           0 : }
     632             : 
     633             : 
     634             : /** \brief Close the connection.
     635             :  *
     636             :  * This function closes the connection by losing the f_bio object.
     637             :  *
     638             :  * As we are at it, we also lose the SSL context since we are not going
     639             :  * to use it anymore either.
     640             :  */
     641           0 : void tcp_bio_client::close()
     642             : {
     643           0 :     f_impl->f_bio.reset();
     644           0 :     f_impl->f_ssl_ctx.reset();
     645           0 : }
     646             : 
     647             : 
     648             : /** \brief Get the socket descriptor.
     649             :  *
     650             :  * This function returns the TCP client socket descriptor. This can be
     651             :  * used to change the descriptor behavior (i.e. make it non-blocking for
     652             :  * example.)
     653             :  *
     654             :  * \note
     655             :  * If the socket was closed, then the function returns -1.
     656             :  *
     657             :  * \warning
     658             :  * This socket is generally managed by the BIO library and thus it may
     659             :  * create unwanted side effects to change the socket under the feet of
     660             :  * the BIO library...
     661             :  *
     662             :  * \return The socket descriptor.
     663             :  */
     664           0 : int tcp_bio_client::get_socket() const
     665             : {
     666           0 :     if(f_impl->f_bio != nullptr)
     667             :     {
     668           0 :         int socket(-1);
     669             : #pragma GCC diagnostic push
     670             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     671           0 :         BIO_get_fd(f_impl->f_bio.get(), &socket);
     672             : #pragma GCC diagnostic pop
     673           0 :         return socket;
     674             :     }
     675             : 
     676           0 :     errno = EBADF;
     677           0 :     return -1;
     678             : }
     679             : 
     680             : 
     681             : /** \brief Return a reference to the address used to connect.
     682             :  *
     683             :  * This function returns a reference to the address used to connect to
     684             :  * a server. This is the server IP address and port information.
     685             :  *
     686             :  * If specified, the host (i.e. the domain name) used can be retrieved
     687             :  * using the get_host() function of the addr object returned.
     688             :  *
     689             :  * \return The address as specified in the constructor.
     690             :  */
     691           0 : addr::addr tcp_bio_client::get_address() const
     692             : {
     693           0 :     return f_address;
     694             : }
     695             : 
     696             : 
     697             : /** \brief Get the address of the client computer.
     698             :  *
     699             :  * This function retrieves the address and port of the client computer
     700             :  * (i.e. yourself). The port will have been auto-selected, so this is
     701             :  * useful if you need that information which is not otherwise available.
     702             :  *
     703             :  * The function caches the information, which will continue to be available
     704             :  * even after the connection is closed (assuming it was retrieved at least
     705             :  * once while the connection was up).
     706             :  *
     707             :  * \return A reference to the remote computer address.
     708             :  */
     709           0 : addr::addr tcp_bio_client::get_client_address()
     710             : {
     711           0 :     if(f_client_address.is_default())
     712             :     {
     713           0 :         int const s(get_socket());
     714           0 :         if(s > 0)
     715             :         {
     716           0 :             f_client_address.set_from_socket(s, false);
     717             :         }
     718             :     }
     719             : 
     720           0 :     return f_client_address;
     721             : }
     722             : 
     723             : 
     724             : /** \brief Read data from the socket.
     725             :  *
     726             :  * A TCP socket is a stream type of socket and one can read data from it
     727             :  * as if it were a regular file. This function reads \p size bytes and
     728             :  * returns. The function returns early if the server closes the connection.
     729             :  *
     730             :  * If your socket is blocking, \p size should be exactly what you are
     731             :  * expecting or this function will block forever or until the server
     732             :  * closes the connection.
     733             :  *
     734             :  * The function returns -1 if an error occurs. The error is available in
     735             :  * errno as expected in the POSIX interface.
     736             :  *
     737             :  * \note
     738             :  * If the connection was closed, this function returns -1.
     739             :  *
     740             :  * \warning
     741             :  * When the function returns zero, it is likely that the server closed
     742             :  * the connection. It may also be that the buffer was empty and that
     743             :  * the BIO decided to return early. Since we use a blocking mechanism
     744             :  * by default, that should not happen.
     745             :  *
     746             :  * \todo
     747             :  * At this point, I do not know for sure whether errno is properly set
     748             :  * or not. It is not unlikely that the BIO library does not keep a clean
     749             :  * errno error since they have their own error management.
     750             :  *
     751             :  * \param[in,out] buf  The buffer where the data is read.
     752             :  * \param[in] size  The size of the buffer.
     753             :  *
     754             :  * \return The number of bytes read from the socket, or -1 on errors.
     755             :  *
     756             :  * \sa read_line()
     757             :  * \sa write()
     758             :  */
     759           0 : int tcp_bio_client::read(char * buf, size_t size)
     760             : {
     761           0 :     if(f_impl->f_bio == nullptr)
     762             :     {
     763           0 :         errno = EBADF;
     764           0 :         return -1;
     765             :     }
     766             : 
     767           0 :     int const r(static_cast<int>(BIO_read(f_impl->f_bio.get(), buf, size)));
     768           0 :     if(r <= -2)
     769             :     {
     770             :         // the BIO is not implemented
     771             :         //
     772           0 :         detail::bio_log_errors();
     773           0 :         errno = EIO;
     774           0 :         return -1;
     775             :     }
     776           0 :     if(r == -1 || r == 0)
     777             :     {
     778           0 :         if(BIO_should_retry(f_impl->f_bio.get()))
     779             :         {
     780           0 :             errno = EAGAIN;
     781           0 :             return 0;
     782             :         }
     783             :         // did we reach the "end of the file"? i.e. did the server
     784             :         // close our connection? (this better replicates what a
     785             :         // normal socket does when reading from a closed socket)
     786             :         //
     787           0 :         if(BIO_eof(f_impl->f_bio.get()))
     788             :         {
     789           0 :             return 0;
     790             :         }
     791           0 :         if(r != 0)
     792             :         {
     793             :             // the BIO generated an error
     794           0 :             detail::bio_log_errors();
     795           0 :             errno = EIO;
     796           0 :             return -1;
     797             :         }
     798             :     }
     799           0 :     return r;
     800             : }
     801             : 
     802             : 
     803             : /** \brief Read one line.
     804             :  *
     805             :  * This function reads one line from the current location up to the next
     806             :  * '\\n' character. We do not have any special handling of the '\\r'
     807             :  * character.
     808             :  *
     809             :  * The function may return 0 (an empty string) when the server closes
     810             :  * the connection.
     811             :  *
     812             :  * \note
     813             :  * If the connection was closed then this function returns -1.
     814             :  *
     815             :  * \warning
     816             :  * A return value of zero can mean "empty line" and not end of file. It
     817             :  * is up to you to know whether your protocol allows for empty lines or
     818             :  * not. If so, you may not be able to make use of this function.
     819             :  *
     820             :  * \param[out] line  The resulting line read from the server. The function
     821             :  *                   first clears the contents.
     822             :  *
     823             :  * \return The number of bytes read from the socket, or -1 on errors.
     824             :  *         If the function returns 0 or more, then the \p line parameter
     825             :  *         represents the characters read on the network without the '\n'.
     826             :  *
     827             :  * \sa read()
     828             :  */
     829           0 : int tcp_bio_client::read_line(std::string & line)
     830             : {
     831           0 :     line.clear();
     832           0 :     int len(0);
     833             :     for(;;)
     834             :     {
     835           0 :         char c;
     836           0 :         int r(read(&c, sizeof(c)));
     837           0 :         if(r <= 0)
     838             :         {
     839           0 :             return len == 0 && r < 0 ? -1 : len;
     840             :         }
     841           0 :         if(c == '\n')
     842             :         {
     843           0 :             return len;
     844             :         }
     845           0 :         ++len;
     846           0 :         line += c;
     847           0 :     }
     848             : }
     849             : 
     850             : 
     851             : /** \brief Write data to the socket.
     852             :  *
     853             :  * A BIO socket is a stream type of socket and one can write data to it
     854             :  * as if it were a regular file. This function writes \p size bytes to
     855             :  * the socket and then returns. This function returns early if the server
     856             :  * closes the connection.
     857             :  *
     858             :  * If your socket is not blocking, less than \p size bytes may be written
     859             :  * to the socket. In that case you are responsible for calling the function
     860             :  * again to write the remainder of the buffer until the function returns
     861             :  * a number of bytes written equal to \p size.
     862             :  *
     863             :  * The function returns -1 if an error occurs. The error is available in
     864             :  * errno as expected in the POSIX interface.
     865             :  *
     866             :  * \note
     867             :  * If the connection was closed, return -1.
     868             :  *
     869             :  * \todo
     870             :  * At this point, I do not know for sure whether errno is properly set
     871             :  * or not. It is not unlikely that the BIO library does not keep a clean
     872             :  * errno error since they have their own error management.
     873             :  *
     874             :  * \param[in] buf  The buffer with the data to send over the socket.
     875             :  * \param[in] size  The number of bytes in buffer to send over the socket.
     876             :  *
     877             :  * \return The number of bytes that were actually accepted by the socket
     878             :  * or -1 if an error occurs.
     879             :  *
     880             :  * \sa read()
     881             :  */
     882           0 : int tcp_bio_client::write(char const * buf, size_t size)
     883             : {
     884             : #ifdef _DEBUG
     885             :     // This write is useful when developing APIs against 3rd party
     886             :     // servers, otherwise, it's just too much debug
     887             :     //SNAP_LOG_TRACE
     888             :     //    << "tcp_bio_client::write(): buf="
     889             :     //    << buf
     890             :     //    << ", size="
     891             :     //    << size
     892             :     //    << SNAP_LOG_SEND;
     893             : #endif
     894           0 :     if(f_impl->f_bio == nullptr)
     895             :     {
     896           0 :         errno = EBADF;
     897           0 :         return -1;
     898             :     }
     899             : 
     900           0 :     int const r(static_cast<int>(BIO_write(f_impl->f_bio.get(), buf, size)));
     901           0 :     if(r <= -2)
     902             :     {
     903             :         // the BIO is not implemented
     904           0 :         detail::bio_log_errors();
     905           0 :         errno = EIO;
     906           0 :         return -1;
     907             :     }
     908           0 :     if(r == -1 || r == 0)
     909             :     {
     910           0 :         if(BIO_should_retry(f_impl->f_bio.get()))
     911             :         {
     912           0 :             errno = EAGAIN;
     913           0 :             return 0;
     914             :         }
     915             :         // the BIO generated an error (TBD should we check BIO_eof() too?)
     916           0 :         detail::bio_log_errors();
     917           0 :         errno = EIO;
     918           0 :         return -1;
     919             :     }
     920           0 :     BIO_flush(f_impl->f_bio.get());
     921           0 :     return r;
     922             : }
     923             : 
     924             : 
     925             : 
     926           6 : } // namespace ed
     927             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13