LCOV - code coverage report
Current view: top level - eventdispatcher - tcp_bio_client.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 211 0.5 %
Date: 2021-09-19 09:06:58 Functions: 2 15 13.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2021  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             : // (TODO: move to .cpp once we have the impl!)
      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             :     snap::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 addr and
     306             :  * \p port specified as parameters. The connection tries to use TLS if
     307             :  * the \p mode parameter is set to MODE_SECURE. Note that you may force
     308             :  * a secure connection using MODE_SECURE_REQUIRED. With MODE_SECURE,
     309             :  * the connection to the server can be obtained even if a secure
     310             :  * connection could not be made to work.
     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] addr  The address of the server to connect to. It must be valid.
     325             :  * \param[in] port  The port the server is listening on.
     326             :  * \param[in] mode  Whether to use SSL when connecting.
     327             :  * \param[in] opt  Additional options.
     328             :  */
     329           0 : tcp_bio_client::tcp_bio_client(
     330             :               std::string const & addr
     331             :             , int port
     332             :             , mode_t mode
     333           0 :             , tcp_bio_options const & opt)
     334           0 :     : f_impl(std::make_shared<detail::tcp_bio_client_impl>())
     335             : {
     336           0 :     if(port < 0 || port >= 65536)
     337             :     {
     338           0 :         throw event_dispatcher_invalid_parameter("invalid port for a client socket");
     339             :     }
     340           0 :     if(addr.empty())
     341             :     {
     342           0 :         throw event_dispatcher_invalid_parameter("an empty address is not valid for a client socket");
     343             :     }
     344             : 
     345           0 :     detail::bio_initialize();
     346             : 
     347           0 :     switch(mode)
     348             :     {
     349           0 :     case mode_t::MODE_SECURE:
     350             :     case mode_t::MODE_ALWAYS_SECURE:
     351             :         {
     352             :             // Use TLS v1 only as all versions of SSL are flawed...
     353             :             // (see below the SSL_CTX_set_options() for additional details
     354             :             // about that since here it does indeed say SSLv23...)
     355             :             //
     356           0 :             std::shared_ptr<SSL_CTX> ssl_ctx; // use a reset(), see SNAP-507
     357           0 :             ssl_ctx.reset(SSL_CTX_new(SSLv23_client_method()), detail::ssl_ctx_deleter);
     358           0 :             if(ssl_ctx == nullptr)
     359             :             {
     360           0 :                 detail::bio_log_errors();
     361           0 :                 throw event_dispatcher_initialization_error("failed creating an SSL_CTX object");
     362             :             }
     363             : 
     364             :             // allow up to `depth` certificates in the chain otherwise fail
     365             :             // (this is not a very strong security feature though); the depth
     366             :             // can be changed before calling this function using a
     367             :             // tcp_bio_options object
     368             :             //
     369           0 :             SSL_CTX_set_verify_depth(ssl_ctx.get(), opt.get_verification_depth());
     370             : 
     371             :             // make sure SSL v2/3 is not used, also compression in SSL is
     372             :             // known to have security issues
     373             :             //
     374           0 :             SSL_CTX_set_options(ssl_ctx.get(), opt.get_ssl_options());
     375             : 
     376             :             // limit the number of ciphers the connection can use
     377           0 :             if(mode == mode_t::MODE_SECURE)
     378             :             {
     379             :                 // this is used by local connections and we get a very strong
     380             :                 // algorithm anyway, but at this point I do not know why it
     381             :                 // does not work with the limited list below...
     382             :                 //
     383             :                 // TODO: test with adding DH support in the server then
     384             :                 //       maybe (probably) that the "HIGH" will work for
     385             :                 //       this entry too...
     386             :                 //
     387           0 :                 SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL");
     388             :             }
     389             :             else
     390             :             {
     391           0 :                 SSL_CTX_set_cipher_list(ssl_ctx.get(), "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
     392             :             }
     393             : 
     394             :             // load root certificates (correct path for Ubuntu?)
     395             :             // TODO: allow client to set the path to certificates
     396           0 :             if(SSL_CTX_load_verify_locations(ssl_ctx.get(), nullptr, "/etc/ssl/certs") != 1)
     397             :             {
     398           0 :                 detail::bio_log_errors();
     399           0 :                 throw event_dispatcher_initialization_error("failed loading verification certificates in an SSL_CTX object");
     400             :             }
     401             :             //SSL_CTX_set_msg_callback(ssl_ctx.get(), ssl_trace);
     402             :             //SSL_CTX_set_msg_callback_arg(ssl_ctx.get(), this);
     403             : 
     404             :             // create a BIO connected to SSL ciphers
     405             :             //
     406           0 :             std::shared_ptr<BIO> bio;
     407           0 :             bio.reset(BIO_new_ssl_connect(ssl_ctx.get()), detail::bio_deleter);
     408           0 :             if(!bio)
     409             :             {
     410           0 :                 detail::bio_log_errors();
     411           0 :                 throw event_dispatcher_initialization_error("failed initializing a BIO object");
     412             :             }
     413             : 
     414             :             // verify that the connection worked
     415             :             //
     416           0 :             SSL * ssl(nullptr);
     417             : #pragma GCC diagnostic push
     418             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     419           0 :             BIO_get_ssl(bio.get(), &ssl);
     420             : #pragma GCC diagnostic pop
     421           0 :             if(ssl == nullptr)
     422             :             {
     423             :                 // TBD: does this mean we would have a plain connection?
     424           0 :                 detail::bio_log_errors();
     425           0 :                 throw event_dispatcher_initialization_error("failed retrieving the SSL contact from BIO object");
     426             :             }
     427             : 
     428             :             // allow automatic retries in case the connection somehow needs
     429             :             // an SSL renegotiation (maybe we should turn that off for cases
     430             :             // where we connect to a secure payment gateway?)
     431             :             //
     432           0 :             SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
     433             : 
     434             :             // setup the Server Name Indication (SNI)
     435             :             //
     436           0 :             bool using_sni(false);
     437           0 :             if(opt.get_sni())
     438             :             {
     439           0 :                 std::string host(opt.get_host());
     440           0 :                 in6_addr ignore;
     441           0 :                 if(host.empty()
     442           0 :                 && inet_pton(AF_INET, addr.c_str(), &ignore) == 0   // must fail
     443           0 :                 && inet_pton(AF_INET6, addr.c_str(), &ignore) == 0) // must fail
     444             :                 {
     445             :                     // addr is not an IP address written as is,
     446             :                     // it must be a hostname
     447             :                     //
     448           0 :                     host = addr;
     449             :                 }
     450           0 :                 if(!host.empty())
     451             :                 {
     452             : #pragma GCC diagnostic push
     453             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     454             :                     // not only old style cast (but it's C, so expected)
     455             :                     // but they want a non-constant pointer!?
     456             :                     //
     457           0 :                     SSL_set_tlsext_host_name(ssl, const_cast<char *>(host.c_str()));
     458             : #pragma GCC diagnostic pop
     459           0 :                     using_sni = true;
     460             :                 }
     461             :             }
     462             : 
     463             :             // TODO: other SSL initialization?
     464             : 
     465             : #pragma GCC diagnostic push
     466             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     467           0 :             BIO_set_conn_hostname(bio.get(), const_cast<char *>(addr.c_str()));
     468           0 :             BIO_set_conn_port(bio.get(), const_cast<char *>(std::to_string(port).c_str()));
     469             : #pragma GCC diagnostic pop
     470             : 
     471             :             // connect to the server (open the socket)
     472             :             //
     473           0 :             if(BIO_do_connect(bio.get()) <= 0)
     474             :             {
     475           0 :                 if(!using_sni)
     476             :                 {
     477           0 :                     SNAP_LOG_WARNING
     478             :                         << "the SNI feature is turned off,"
     479             :                            " often failure to connect with SSL is because the"
     480             :                            " SSL Hello message is missing the SNI (Server Name In)."
     481           0 :                            " See the tcp_bio_options::set_sni()."
     482             :                         << SNAP_LOG_SEND;
     483             :                 }
     484           0 :                 detail::bio_log_errors();
     485           0 :                 throw event_dispatcher_initialization_error("SSL BIO_do_connect() failed connecting BIO object to server");
     486             :             }
     487             : 
     488             :             // encryption handshake
     489             :             //
     490           0 :             if(BIO_do_handshake(bio.get()) != 1)
     491             :             {
     492           0 :                 if(!using_sni)
     493             :                 {
     494           0 :                     SNAP_LOG_WARNING
     495             :                         << "the SNI feature is turned off,"
     496             :                            " often failure to connect with SSL is because the"
     497             :                            " SSL Hello message is missing the SNI (Server Name In)."
     498           0 :                            " See the tcp_bio_options::set_sni()."
     499             :                         << SNAP_LOG_SEND;
     500             :                 }
     501           0 :                 detail::bio_log_errors();
     502             :                 throw event_dispatcher_initialization_error("failed establishing a secure BIO connection with server, handshake failed."
     503             :                             " Often such failures to process SSL is because the SSL Hello message is missing the SNI (Server Name In)."
     504           0 :                             " See the tcp_bio_options::set_sni().");
     505             :             }
     506             : 
     507             :             // verify that the peer certificate was signed by a
     508             :             // recognized root authority
     509             :             //
     510           0 :             if(SSL_get_peer_certificate(ssl) == nullptr)
     511             :             {
     512           0 :                 detail::bio_log_errors();
     513           0 :                 throw event_dispatcher_initialization_error("peer failed presenting a certificate for security verification");
     514             :             }
     515             : 
     516             :             // XXX: check that the call below is similar to the example
     517             :             //      usage of SSL_CTX_set_verify() which checks the name
     518             :             //      of the certificate, etc.
     519             :             //
     520           0 :             if(SSL_get_verify_result(ssl) != X509_V_OK)
     521             :             {
     522           0 :                 if(mode != mode_t::MODE_SECURE)
     523             :                 {
     524           0 :                     detail::bio_log_errors();
     525           0 :                     throw event_dispatcher_initialization_error("peer certificate could not be verified");
     526             :                 }
     527           0 :                 SNAP_LOG_WARNING
     528           0 :                     << "connecting with SSL but certificate verification failed."
     529             :                     << SNAP_LOG_SEND;
     530             :             }
     531             : 
     532             :             // it worked, save the results
     533             :             //
     534           0 :             f_impl->f_ssl_ctx.swap(ssl_ctx);
     535           0 :             f_impl->f_bio.swap(bio);
     536             : 
     537             :             // secure connection ready
     538             :             //
     539           0 :             char const * cipher_name(SSL_get_cipher(ssl));
     540           0 :             int cipher_bits(0);
     541           0 :             SSL_get_cipher_bits(ssl, &cipher_bits);
     542           0 :             SNAP_LOG_DEBUG
     543           0 :                 << "connected with SSL cipher \""
     544             :                 << cipher_name
     545           0 :                 << "\" representing "
     546             :                 << cipher_bits
     547             :                 << " bits of encryption."
     548           0 :                 << SNAP_LOG_SEND;
     549             :         }
     550           0 :         break;
     551             : 
     552           0 :     case mode_t::MODE_PLAIN:
     553             :         {
     554             :             // create a plain BIO connection
     555             :             //
     556           0 :             std::shared_ptr<BIO> bio;  // use reset(), see SNAP-507
     557           0 :             bio.reset(BIO_new(BIO_s_connect()), detail::bio_deleter);
     558           0 :             if(!bio)
     559             :             {
     560           0 :                 detail::bio_log_errors();
     561           0 :                 throw event_dispatcher_initialization_error("failed initializing a BIO object");
     562             :             }
     563             : 
     564             : #pragma GCC diagnostic push
     565             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     566           0 :             BIO_set_conn_hostname(bio.get(), const_cast<char *>(addr.c_str()));
     567           0 :             BIO_set_conn_port(bio.get(), const_cast<char *>(std::to_string(port).c_str()));
     568             : #pragma GCC diagnostic pop
     569             : 
     570             :             // connect to the server (open the socket)
     571             :             //
     572           0 :             if(BIO_do_connect(bio.get()) <= 0)
     573             :             {
     574           0 :                 detail::bio_log_errors();
     575           0 :                 throw event_dispatcher_initialization_error("failed connecting BIO object to server");
     576             :             }
     577             : 
     578             :             // it worked, save the results
     579             :             //
     580           0 :             f_impl->f_bio.swap(bio);
     581             : 
     582             :             // plain connection ready
     583             :         }
     584           0 :         break;
     585             : 
     586             :     }
     587             : 
     588           0 :     if(opt.get_keepalive())
     589             :     {
     590             :         // retrieve the socket (we are still in the constructor so avoid
     591             :         // calling other functions...)
     592             :         //
     593           0 :         int socket(-1);
     594             : #pragma GCC diagnostic push
     595             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     596           0 :         BIO_get_fd(f_impl->f_bio.get(), &socket);
     597             : #pragma GCC diagnostic pop
     598           0 :         if(socket >= 0)
     599             :         {
     600             :             // if this call fails, we ignore the error, but still log the event
     601             :             //
     602           0 :             int optval(1);
     603           0 :             socklen_t const optlen(sizeof(optval));
     604           0 :             if(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) != 0)
     605             :             {
     606           0 :                 SNAP_LOG_WARNING
     607           0 :                     << "an error occurred trying to mark client socket with SO_KEEPALIVE."
     608             :                     << SNAP_LOG_SEND;
     609             :             }
     610             :         }
     611             :     }
     612           0 : }
     613             : 
     614             : 
     615             : /** \brief Create a BIO client object from an actual BIO pointer.
     616             :  *
     617             :  * This function is called by the server whenever it accepts a new BIO
     618             :  * connection. The server then can return the tcp_bio_client object instead
     619             :  * of a BIO object.
     620             :  *
     621             :  * The BIO is saved directly in the f_impl class (the server is given access.)
     622             :  */
     623           0 : tcp_bio_client::tcp_bio_client()
     624           0 :     : f_impl(std::make_shared<detail::tcp_bio_client_impl>())
     625             : {
     626           0 : }
     627             : 
     628             : 
     629             : /** \brief Clean up the BIO client object.
     630             :  *
     631             :  * This function cleans up the BIO client object by freeing the SSL_CTX
     632             :  * and the BIO objects.
     633             :  */
     634           0 : tcp_bio_client::~tcp_bio_client()
     635             : {
     636             :     // f_bio and f_ssl_ctx are allocated using shared pointers with
     637             :     // a deleter so we have nothing to do here.
     638           0 : }
     639             : 
     640             : 
     641             : /** \brief Close the connection.
     642             :  *
     643             :  * This function closes the connection by losing the f_bio object.
     644             :  *
     645             :  * As we are at it, we also lose the SSL context since we are not going
     646             :  * to use it anymore either.
     647             :  */
     648           0 : void tcp_bio_client::close()
     649             : {
     650           0 :     f_impl->f_bio.reset();
     651           0 :     f_impl->f_ssl_ctx.reset();
     652           0 : }
     653             : 
     654             : 
     655             : /** \brief Get the socket descriptor.
     656             :  *
     657             :  * This function returns the TCP client socket descriptor. This can be
     658             :  * used to change the descriptor behavior (i.e. make it non-blocking for
     659             :  * example.)
     660             :  *
     661             :  * \note
     662             :  * If the socket was closed, then the function returns -1.
     663             :  *
     664             :  * \warning
     665             :  * This socket is generally managed by the BIO library and thus it may
     666             :  * create unwanted side effects to change the socket under the feet of
     667             :  * the BIO library...
     668             :  *
     669             :  * \return The socket descriptor.
     670             :  */
     671           0 : int tcp_bio_client::get_socket() const
     672             : {
     673           0 :     if(f_impl->f_bio)
     674             :     {
     675           0 :         int c;
     676             : #pragma GCC diagnostic push
     677             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     678           0 :         BIO_get_fd(f_impl->f_bio.get(), &c);
     679             : #pragma GCC diagnostic pop
     680           0 :         return c;
     681             :     }
     682             : 
     683           0 :     return -1;
     684             : }
     685             : 
     686             : 
     687             : /** \brief Get the TCP client port.
     688             :  *
     689             :  * This function returns the port used when creating the TCP client.
     690             :  * Note that this is the port the server is listening to and not the port
     691             :  * the TCP client is currently connected to.
     692             :  *
     693             :  * \note
     694             :  * If the connection was closed, return -1.
     695             :  *
     696             :  * \return The TCP client port.
     697             :  */
     698           0 : int tcp_bio_client::get_port() const
     699             : {
     700           0 :     if(f_impl->f_bio)
     701             :     {
     702           0 :         return std::atoi(BIO_get_conn_port(f_impl->f_bio.get()));
     703             :     }
     704             : 
     705           0 :     return -1;
     706             : }
     707             : 
     708             : 
     709             : /** \brief Get the TCP server address.
     710             :  *
     711             :  * This function returns the address used when creating the TCP address as is.
     712             :  * Note that this is the address of the server where the client is connected
     713             :  * and not the address where the client is running (although it may be the
     714             :  * same.)
     715             :  *
     716             :  * Use the get_client_addr() function to retrieve the client's TCP address.
     717             :  *
     718             :  * \note
     719             :  * If the connection was closed, this function returns "".
     720             :  *
     721             :  * \return The TCP client address.
     722             :  */
     723           0 : std::string tcp_bio_client::get_addr() const
     724             : {
     725           0 :     if(f_impl->f_bio)
     726             :     {
     727           0 :         return BIO_get_conn_hostname(f_impl->f_bio.get());
     728             :     }
     729             : 
     730           0 :     return "";
     731             : }
     732             : 
     733             : 
     734             : /** \brief Get the TCP client port.
     735             :  *
     736             :  * This function retrieve the port of the client (used on your computer).
     737             :  * This is retrieved from the socket using the getsockname() function.
     738             :  *
     739             :  * \return The port or -1 if it cannot be determined.
     740             :  */
     741           0 : int tcp_bio_client::get_client_port() const
     742             : {
     743             :     // get_socket() returns -1 if f_bio is nullptr
     744             :     //
     745           0 :     int const s(get_socket());
     746           0 :     if(s < 0)
     747             :     {
     748           0 :         return -1;
     749             :     }
     750             : 
     751           0 :     sockaddr addr;
     752           0 :     socklen_t len(sizeof(addr));
     753           0 :     int const r(getsockname(s, &addr, &len));
     754           0 :     if(r != 0)
     755             :     {
     756           0 :         return -1;
     757             :     }
     758             :     // Note: I know the port is at the exact same location in both
     759             :     //       structures in Linux but it could change on other Unices
     760           0 :     switch(addr.sa_family)
     761             :     {
     762           0 :     case AF_INET:
     763             :         // IPv4
     764           0 :         return reinterpret_cast<sockaddr_in *>(&addr)->sin_port;
     765             : 
     766           0 :     case AF_INET6:
     767             :         // IPv6
     768           0 :         return reinterpret_cast<sockaddr_in6 *>(&addr)->sin6_port;
     769             : 
     770           0 :     default:
     771           0 :         return -1;
     772             : 
     773             :     }
     774             :     snap::NOT_REACHED();
     775             : }
     776             : 
     777             : 
     778             : /** \brief Get the TCP client address.
     779             :  *
     780             :  * This function retrieve the IP address of the client (your computer).
     781             :  * This is retrieved from the socket using the getsockname() function.
     782             :  *
     783             :  * \note
     784             :  * The function returns an empty string if the connection was lost
     785             :  * or purposefully closed.
     786             :  *
     787             :  * \return The IP address as a string.
     788             :  */
     789           0 : std::string tcp_bio_client::get_client_addr() const
     790             : {
     791             :     // the socket may be invalid, i.e. f_bio may have been deallocated.
     792             :     //
     793           0 :     int const s(get_socket());
     794           0 :     if(s < 0)
     795             :     {
     796           0 :         return std::string();
     797             :     }
     798             : 
     799           0 :     sockaddr addr;
     800           0 :     socklen_t len(sizeof(addr));
     801           0 :     int const r(getsockname(s, &addr, &len));
     802           0 :     if(r != 0)
     803             :     {
     804           0 :         throw event_dispatcher_runtime_error("failed reading address");
     805             :     }
     806           0 :     char buf[BUFSIZ];
     807           0 :     switch(addr.sa_family)
     808             :     {
     809           0 :     case AF_INET:
     810             :         // TODO: verify that 'r' >= sizeof(something)
     811           0 :         inet_ntop(AF_INET, &reinterpret_cast<struct sockaddr_in *>(&addr)->sin_addr, buf, sizeof(buf));
     812           0 :         break;
     813             : 
     814           0 :     case AF_INET6:
     815             :         // TODO: verify that 'r' >= sizeof(something)
     816           0 :         inet_ntop(AF_INET6, &reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_addr, buf, sizeof(buf));
     817           0 :         break;
     818             : 
     819           0 :     default:
     820           0 :         throw event_dispatcher_runtime_error("unknown address family");
     821             : 
     822             :     }
     823           0 :     return buf;
     824             : }
     825             : 
     826             : 
     827             : /** \brief Read data from the socket.
     828             :  *
     829             :  * A TCP socket is a stream type of socket and one can read data from it
     830             :  * as if it were a regular file. This function reads \p size bytes and
     831             :  * returns. The function returns early if the server closes the connection.
     832             :  *
     833             :  * If your socket is blocking, \p size should be exactly what you are
     834             :  * expecting or this function will block forever or until the server
     835             :  * closes the connection.
     836             :  *
     837             :  * The function returns -1 if an error occurs. The error is available in
     838             :  * errno as expected in the POSIX interface.
     839             :  *
     840             :  * \note
     841             :  * If the connection was closed, this function returns -1.
     842             :  *
     843             :  * \warning
     844             :  * When the function returns zero, it is likely that the server closed
     845             :  * the connection. It may also be that the buffer was empty and that
     846             :  * the BIO decided to return early. Since we use a blocking mechanism
     847             :  * by default, that should not happen.
     848             :  *
     849             :  * \todo
     850             :  * At this point, I do not know for sure whether errno is properly set
     851             :  * or not. It is not unlikely that the BIO library does not keep a clean
     852             :  * errno error since they have their own error management.
     853             :  *
     854             :  * \param[in,out] buf  The buffer where the data is read.
     855             :  * \param[in] size  The size of the buffer.
     856             :  *
     857             :  * \return The number of bytes read from the socket, or -1 on errors.
     858             :  *
     859             :  * \sa read_line()
     860             :  * \sa write()
     861             :  */
     862           0 : int tcp_bio_client::read(char * buf, size_t size)
     863             : {
     864           0 :     if(!f_impl->f_bio)
     865             :     {
     866           0 :         errno = EBADF;
     867           0 :         return -1;
     868             :     }
     869             : 
     870           0 :     int const r(static_cast<int>(BIO_read(f_impl->f_bio.get(), buf, size)));
     871           0 :     if(r <= -2)
     872             :     {
     873             :         // the BIO is not implemented
     874             :         //
     875           0 :         detail::bio_log_errors();
     876           0 :         errno = EIO;
     877           0 :         return -1;
     878             :     }
     879           0 :     if(r == -1 || r == 0)
     880             :     {
     881           0 :         if(BIO_should_retry(f_impl->f_bio.get()))
     882             :         {
     883           0 :             errno = EAGAIN;
     884           0 :             return 0;
     885             :         }
     886             :         // did we reach the "end of the file"? i.e. did the server
     887             :         // close our connection? (this better replicates what a
     888             :         // normal socket does when reading from a closed socket)
     889             :         //
     890           0 :         if(BIO_eof(f_impl->f_bio.get()))
     891             :         {
     892           0 :             return 0;
     893             :         }
     894           0 :         if(r != 0)
     895             :         {
     896             :             // the BIO generated an error
     897           0 :             detail::bio_log_errors();
     898           0 :             errno = EIO;
     899           0 :             return -1;
     900             :         }
     901             :     }
     902           0 :     return r;
     903             : }
     904             : 
     905             : 
     906             : /** \brief Read one line.
     907             :  *
     908             :  * This function reads one line from the current location up to the next
     909             :  * '\\n' character. We do not have any special handling of the '\\r'
     910             :  * character.
     911             :  *
     912             :  * The function may return 0 (an empty string) when the server closes
     913             :  * the connection.
     914             :  *
     915             :  * \note
     916             :  * If the connection was closed then this function returns -1.
     917             :  *
     918             :  * \warning
     919             :  * A return value of zero can mean "empty line" and not end of file. It
     920             :  * is up to you to know whether your protocol allows for empty lines or
     921             :  * not. If so, you may not be able to make use of this function.
     922             :  *
     923             :  * \param[out] line  The resulting line read from the server. The function
     924             :  *                   first clears the contents.
     925             :  *
     926             :  * \return The number of bytes read from the socket, or -1 on errors.
     927             :  *         If the function returns 0 or more, then the \p line parameter
     928             :  *         represents the characters read on the network without the '\n'.
     929             :  *
     930             :  * \sa read()
     931             :  */
     932           0 : int tcp_bio_client::read_line(std::string & line)
     933             : {
     934           0 :     line.clear();
     935           0 :     int len(0);
     936             :     for(;;)
     937             :     {
     938           0 :         char c;
     939           0 :         int r(read(&c, sizeof(c)));
     940           0 :         if(r <= 0)
     941             :         {
     942           0 :             return len == 0 && r < 0 ? -1 : len;
     943             :         }
     944           0 :         if(c == '\n')
     945             :         {
     946           0 :             return len;
     947             :         }
     948           0 :         ++len;
     949           0 :         line += c;
     950           0 :     }
     951             : }
     952             : 
     953             : 
     954             : /** \brief Write data to the socket.
     955             :  *
     956             :  * A BIO socket is a stream type of socket and one can write data to it
     957             :  * as if it were a regular file. This function writes \p size bytes to
     958             :  * the socket and then returns. This function returns early if the server
     959             :  * closes the connection.
     960             :  *
     961             :  * If your socket is not blocking, less than \p size bytes may be written
     962             :  * to the socket. In that case you are responsible for calling the function
     963             :  * again to write the remainder of the buffer until the function returns
     964             :  * a number of bytes written equal to \p size.
     965             :  *
     966             :  * The function returns -1 if an error occurs. The error is available in
     967             :  * errno as expected in the POSIX interface.
     968             :  *
     969             :  * \note
     970             :  * If the connection was closed, return -1.
     971             :  *
     972             :  * \todo
     973             :  * At this point, I do not know for sure whether errno is properly set
     974             :  * or not. It is not unlikely that the BIO library does not keep a clean
     975             :  * errno error since they have their own error management.
     976             :  *
     977             :  * \param[in] buf  The buffer with the data to send over the socket.
     978             :  * \param[in] size  The number of bytes in buffer to send over the socket.
     979             :  *
     980             :  * \return The number of bytes that were actually accepted by the socket
     981             :  * or -1 if an error occurs.
     982             :  *
     983             :  * \sa read()
     984             :  */
     985           0 : int tcp_bio_client::write(char const * buf, size_t size)
     986             : {
     987             : #ifdef _DEBUG
     988             :     // This write is useful when developing APIs against 3rd party
     989             :     // servers, otherwise, it's just too much debug
     990             :     //SNAP_LOG_TRACE
     991             :     //    << "tcp_bio_client::write(): buf="
     992             :     //    << buf
     993             :     //    << ", size="
     994             :     //    << size
     995             :     //    << SNAP_LOG_SEND;
     996             : #endif
     997           0 :     if(!f_impl->f_bio)
     998             :     {
     999           0 :         errno = EBADF;
    1000           0 :         return -1;
    1001             :     }
    1002             : 
    1003           0 :     int const r(static_cast<int>(BIO_write(f_impl->f_bio.get(), buf, size)));
    1004           0 :     if(r <= -2)
    1005             :     {
    1006             :         // the BIO is not implemented
    1007           0 :         detail::bio_log_errors();
    1008           0 :         errno = EIO;
    1009           0 :         return -1;
    1010             :     }
    1011           0 :     if(r == -1 || r == 0)
    1012             :     {
    1013           0 :         if(BIO_should_retry(f_impl->f_bio.get()))
    1014             :         {
    1015           0 :             errno = EAGAIN;
    1016           0 :             return 0;
    1017             :         }
    1018             :         // the BIO generated an error (TBD should we check BIO_eof() too?)
    1019           0 :         detail::bio_log_errors();
    1020           0 :         errno = EIO;
    1021           0 :         return -1;
    1022             :     }
    1023           0 :     BIO_flush(f_impl->f_bio.get());
    1024           0 :     return r;
    1025             : }
    1026             : 
    1027             : 
    1028             : 
    1029           6 : } // namespace ed
    1030             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13