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-08 02:52:36 Functions: 2 15 13.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12