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

Generated by: LCOV version 1.13