LCOV - code coverage report
Current view: top level - eventdispatcher - tcp_bio_client.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 193 0.5 %
Date: 2019-08-10 01:48:51 Functions: 2 15 13.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12