c++ - boost ssl connection procedure fails -




i trying combine famous boost ssl client/server connection examples single program. kind reference, base classes this:

#include <cstdlib> #include <iostream> #include <boost/bind.hpp> #include <boost/asio.hpp> #include <boost/asio/ssl.hpp>  namespace bt { // // client.cpp // ~~~~~~~~~~ // // copyright (c) 2003-2011 christopher m. kohlhoff (chris @ kohlhoff dot com) // // distributed under boost software license, version 1.0. (see accompanying // file license_1_0.txt or copy @ http://www.boost.org/license_1_0.txt) //   enum { max_length = 1024 };  class client { public:   client(boost::asio::io_service& io_service, boost::asio::ssl::context& context,       boost::asio::ip::tcp::resolver::iterator endpoint_iterator)     : socket_(io_service, context)   {     boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;     socket_.lowest_layer().async_connect(endpoint,         boost::bind(&client::handle_connect, this,           boost::asio::placeholders::error, ++endpoint_iterator));   }    void handle_connect(const boost::system::error_code& error,       boost::asio::ip::tcp::resolver::iterator endpoint_iterator)   {       std::cout << "handle_connect\n";     if (!error)     {         std::cout << "handle_connect no error\n";       socket_.async_handshake(boost::asio::ssl::stream_base::client,           boost::bind(&client::handle_handshake, this,             boost::asio::placeholders::error));     }     else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())     {         std::cout << "handle_connect retry!\n";       socket_.lowest_layer().close();       boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;       socket_.lowest_layer().async_connect(endpoint,           boost::bind(&client::handle_connect, this,             boost::asio::placeholders::error, ++endpoint_iterator));     }     else     {       std::cout << "connect failed: " << error << "\n";     }   }    void handle_handshake(const boost::system::error_code& error)   {       std::cout << "client handle_handshake\n";     if (!error)     {       std::cout << "enter message: "; //      std::cin.getline(request_, max_length);       sprintf(request_, "%s", "hi testing...");       size_t request_length = strlen(request_);        boost::asio::async_write(socket_,           boost::asio::buffer(request_, request_length),           boost::bind(&client::handle_write, this,             boost::asio::placeholders::error,             boost::asio::placeholders::bytes_transferred));     }     else     {       std::cout << "handshake failed: " << error << "\n";     }   }    void handle_write(const boost::system::error_code& error,       size_t bytes_transferred)   {     if (!error)     {       boost::asio::async_read(socket_,           boost::asio::buffer(reply_, bytes_transferred),           boost::bind(&client::handle_read, this,             boost::asio::placeholders::error,             boost::asio::placeholders::bytes_transferred));     }     else     {       std::cout << "write failed: " << error << "\n";     }   }    void handle_read(const boost::system::error_code& error,       size_t bytes_transferred)   {     if (!error)     {       std::cout << "reply: ";       std::cout.write(reply_, bytes_transferred);       std::cout << "\n";     }     else     {       std::cout << "read failed: " << error << "\n";     }   }  private:   boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;   char request_[max_length];   char reply_[max_length]; };   // // server.cpp // ~~~~~~~~~~ // // copyright (c) 2003-2011 christopher m. kohlhoff (chris @ kohlhoff dot com) // // distributed under boost software license, version 1.0. (see accompanying // file license_1_0.txt or copy @ http://www.boost.org/license_1_0.txt) //   typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;  class session { public:   session(boost::asio::io_service& io_service, boost::asio::ssl::context& context)     : socket_(io_service, context)   {   }    ssl_socket::lowest_layer_type& socket()   {     return socket_.lowest_layer();   }    void start()   {       std::cout << "session start->handshake\n";     socket_.async_handshake(boost::asio::ssl::stream_base::server,         boost::bind(&session::handle_handshake, this,           boost::asio::placeholders::error));   }    void handle_handshake(const boost::system::error_code& error)   {       std::cout << "session handle_handshake\n";     if (!error)     {       socket_.async_read_some(boost::asio::buffer(data_, max_length),           boost::bind(&session::handle_read, this,             boost::asio::placeholders::error,             boost::asio::placeholders::bytes_transferred));     }     else     {       delete this;     }   }    void handle_read(const boost::system::error_code& error,       size_t bytes_transferred)   {     if (!error)     {       boost::asio::async_write(socket_,           boost::asio::buffer(data_, bytes_transferred),           boost::bind(&session::handle_write, this,             boost::asio::placeholders::error));     }     else     {       delete this;     }   }    void handle_write(const boost::system::error_code& error)   {     if (!error)     {       socket_.async_read_some(boost::asio::buffer(data_, max_length),           boost::bind(&session::handle_read, this,             boost::asio::placeholders::error,             boost::asio::placeholders::bytes_transferred));     }     else     {       delete this;     }   }  private:   ssl_socket socket_;   enum { max_length = 1024 };   char data_[max_length]; };  class server { public:   server(boost::asio::io_service& io_service, unsigned short port)     : io_service_(io_service),       acceptor_(io_service,           boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),       context_(io_service, boost::asio::ssl::context::sslv23)   {       //std::cout << "server()\n";     context_.set_options(         boost::asio::ssl::context::default_workarounds         | boost::asio::ssl::context::no_sslv2         | boost::asio::ssl::context::single_dh_use);     context_.set_password_callback(boost::bind(&server::get_password, this));     context_.use_certificate_chain_file("server.crt");     context_.use_private_key_file("server.key", boost::asio::ssl::context::pem);     context_.use_tmp_dh_file("dh1024.pem");      session* new_session = new session(io_service_, context_);     acceptor_.async_accept(new_session->socket(),         boost::bind(&server::handle_accept, this, new_session,           boost::asio::placeholders::error));   }    std::string get_password() const   {     return "test";   }    void handle_accept(session* new_session,       const boost::system::error_code& error)   {       std::cout << "server() handle_accept\n";     if (!error)     {         std::cout << "server() handle_accept  !error\n";       new_session->start();       new_session = new session(io_service_, context_);       acceptor_.async_accept(new_session->socket(),           boost::bind(&server::handle_accept, this, new_session,             boost::asio::placeholders::error));     }     else     {         std::cout << "server() handle_accept  error:" << error.message() << std::endl;       delete new_session;     }   }  private:   boost::asio::io_service& io_service_;   boost::asio::ip::tcp::acceptor acceptor_;   boost::asio::ssl::context context_; };  }//namespace bt 

and the main program is:

boost_auto_test_case(accept_ssl_connection_1) {     boost::asio::io_service io_service_1;     boost::asio::io_service io_service_2;      int port = random_port();     std::stringstream i("");     << port;     std::cout << "port is:" << i.str() << std::endl;     //server     bt::server(io_service_1, port);     //client     boost::asio::ip::tcp::resolver resolver(io_service_2);     boost::asio::ip::tcp::resolver::query query("127.0.0.1", i.str());     boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);      boost::asio::ssl::context ctx(io_service_2, boost::asio::ssl::context::sslv23);     ctx.set_verify_mode(boost::asio::ssl::context::verify_peer);     ctx.load_verify_file("server.crt");      bt::client c(io_service_2, ctx, iterator);      boost::thread thread1(boost::bind(&boost::asio::io_service::run, &io_service_1));     boost::thread thread2(boost::bind(&boost::asio::io_service::run, &io_service_2));      thread1.join();     thread2.join(); } 

and here output getting:

port is:7200 server() handle_accept handle_connect connect failed: system:111 server() handle_accept  error:operation canceled 

the program works if clien , server built , run individually. guess have mistake in io_service usage. please me detect issue?

1. style

i suggest put more effort in making code readable.

code humans read, not computers

in case, extreme brevity like

bt::client c(...); 

leads bugs like

bt::server(io_service_1, port); 

there's not lot of difference - intended - variable declaration

bt::server s(io_service_1, port); 

otherwise, newly constructed server immediately destructed , thereby cancels pending operations.

2. debugging

try present readable messages:

std::cout << "connect failed: " << error.message() << "\n"; std::cout << "handshake failed: " << error.message() << "\n"; std::cout << "write failed: " << error.message() << "\n"; std::cout << "read failed: " << error.message() << "\n"; std::cout << "server() handle_accept  error:" << error.message() << std::endl; 

this tell "125" means "operation aborted" etc.. made me add little trace here , there:

~session() { std::cout << "deleting session!\n"; } ~server() { std::cout << "deleting server!\n"; } 

2. asio review, more style

  1. instead of doing things manually, prefer composed operations defined in boost:

    client(ba::io_service &io_service, ssl::context &context, tcp::resolver::iterator endpoint_iterator)         : socket_(io_service, context)  {     ba::async_connect(socket_.lowest_layer(), endpoint_iterator,                       boost::bind(&client::handle_connect, this, bap::error)); }  void handle_connect(const boost::system::error_code &error) {     std::cout << "handle_connect\n";     if (!error) {         std::cout << "handle_connect no error\n";         socket_.async_handshake(ssl::stream_base::client, boost::bind(&client::handle_handshake, this, bap::error));     } else {         std::cout << "connect failed: " << error.message() << "\n";     } } 

    this whole iterator dance. less error-prone.

  2. use namespace aliases readable/manageable lines

    using boost::asio::ip::tcp; namespace ba = boost::asio; namespace bap = boost::asio::placeholders; namespace ssl = boost::asio::ssl; 
  3. use smart pointers (delete this? ugh)

  4. consider using 1 io_service. using 2 doesn't add anything, really, , names didn't clarify thing. in fact, first minutes of staring @ code had me dis-entangling code client , server, painstakingly verifying didn't mistakenly use wrong service, leading premature run() completion.

  5. account race conditions. in code, server , client run independently unsynchronized. @ least add delay:

    boost::this_thread::sleep_for(boost::chrono::seconds(1)); 

    to avoid client connecting server before started accepting connections.

  6. prefer boost::thread_group on lose threads:

    boost::thread_group tg; // ... tg.create_thread(boost::bind(&ba::io_service::run, &io_service_1)); // ... tg.create_thread(boost::bind(&ba::io_service::run, &io_service_2)); // ... tg.join_all(); 
  7. in fact, 1 io_service , 1 thread, sidestep of above (the async operations synchronized due implicit strand)

  8. use higherlevel standard library features (e.g. std::to_string(int) instead of std::ostringstream; if cannot use c++11, use boost::lexical_cast or write own to_string-type helper function).

  9. if address hardcoded loopback, no need "resolve" anything: connect tcp::endpoint{{}, port}

  10. consider moving ctx client (like moved ssl params server class too)

  11. prefer boost::array/std::array on raw arrays (request_ , reply_)

  12. why read many bytes sent? did mean

    ba::async_read(socket_, ba::buffer(reply_, bytes_transferred),                boost::bind(&client::handle_read, this, bap::error, bap::bytes_transferred)); 

    i'd expect like

    ba::async_read(socket_, ba::buffer(reply_, reply.size()), // assuming array<>, see previous                boost::bind(&client::handle_read, this, bap::error, bap::bytes_transferred)); 
  13. consider composed operations on read_some again. read_some may not read complete request. consider adding framing protocol or sending request length front.

  14. avoid code duplication: async_accept coded twice. instead make separate function , call twice:

    void do_accept() {     session::ptr new_session = boost::make_shared<session>(io_service_, context_);     acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, bap::error)); } 

bonus

  1. add deadline accept can stop server @ idle time interval

  2. since using smart pointers (aren't you?) it's easy add session shutdown @ place (session::close())

  3. let's 2 client price of one, fun

live on coliru

//#define boost_asio_enable_handler_tracking 1 #include <boost/asio.hpp> #include <boost/asio/ssl.hpp> #include <boost/bind.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/make_shared.hpp> #include <cstdlib> #include <iostream>  using boost::asio::ip::tcp; namespace ba = boost::asio; namespace bap = boost::asio::placeholders; namespace ssl = boost::asio::ssl;  namespace bt {  enum { max_length = 1024, idle_timeout_seconds = 2 };  class client {   public:     client(ba::io_service &io_service, tcp::resolver::iterator endpoint_iterator, std::string const& request)             : ctx_(io_service, ssl::context::sslv23),               socket_(io_service, ctx_),               request_(request)     {         ctx_.set_verify_mode(ssl::context::verify_peer);         ctx_.load_verify_file("server.crt");         ba::async_connect(socket_.lowest_layer(), endpoint_iterator,                           boost::bind(&client::handle_connect, this, bap::error));     }      void handle_connect(const boost::system::error_code &error) {         std::cout << "handle_connect\n";         if (!error) {             std::cout << "handle_connect no error\n";             socket_.async_handshake(ssl::stream_base::client, boost::bind(&client::handle_handshake, this, bap::error));         } else {             std::cout << "connect failed: " << error.message() << "\n";         }     }      void handle_handshake(const boost::system::error_code &error) {         std::cout << "client handle_handshake\n";         if (!error) {             ba::async_write(socket_, ba::buffer(request_),                             boost::bind(&client::handle_write, this, bap::error, bap::bytes_transferred));         } else {             std::cout << "handshake failed: " << error.message() << "\n";         }     }      void handle_write(const boost::system::error_code &error, size_t bytes_transferred) {         if (!error) {             ba::async_read(socket_, ba::buffer(reply_, bytes_transferred),                            boost::bind(&client::handle_read, this, bap::error, bap::bytes_transferred));         } else {             std::cout << "write failed: " << error.message() << "\n";         }     }      void handle_read(const boost::system::error_code &error, size_t bytes_transferred) {         if (!error) {             std::cout << "reply: ";             std::cout.write(reply_.data(), bytes_transferred);             std::cout << "\n";         } else {             std::cout << "read failed: " << error.message() << "\n";         }     }    private:     ssl::context ctx_;     ssl::stream<tcp::socket> socket_;     std::string request_;     std::array<char, max_length> reply_; };  class session : public boost::enable_shared_from_this<session> {   public:     using ptr = boost::shared_ptr<session>;     session(ba::io_service &io_service, ssl::context &context) : socket_(io_service, context) {}      typedef ssl::stream<tcp::socket> ssl_socket;     ssl_socket::lowest_layer_type &socket() { return socket_.lowest_layer(); }      void start() {         std::cout << "session start->handshake\n";         socket_.async_handshake(ssl::stream_base::server, boost::bind(&session::handle_handshake, shared_from_this(), bap::error));     }      void handle_handshake(const boost::system::error_code &error) {         std::cout << "session handle_handshake\n";         if (error) return;         socket_.async_read_some(ba::buffer(data_),                 boost::bind(&session::handle_read, shared_from_this(), bap::error, bap::bytes_transferred));     }      void handle_read(const boost::system::error_code &error, size_t bytes_transferred) {         if (error) return;         ba::async_write(socket_, ba::buffer(data_, bytes_transferred),                 boost::bind(&session::handle_write, shared_from_this(), bap::error));     }      void handle_write(const boost::system::error_code &error) {         if (error) return;         socket_.async_read_some(ba::buffer(data_),                 boost::bind(&session::handle_read, shared_from_this(), bap::error, bap::bytes_transferred));     }      void close() {         socket_.get_io_service().post([this] {                 std::cout << "session::close()\n";                 socket_.lowest_layer().cancel();                 socket_.lowest_layer().close();             });     }      ~session() { std::cout << "deleting session\n"; }    private:     ssl_socket socket_;     std::array<char, max_length> data_; };  class server {   public:     server(ba::io_service &io_service, unsigned short port)             : io_service_(io_service), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),               context_(io_service, ssl::context::sslv23),               deadline_(io_service)     {         // std::cout << "server()\n";         context_.set_options(ssl::context::default_workarounds | ssl::context::no_sslv2 | ssl::context::single_dh_use);         context_.set_password_callback(boost::bind(&server::get_password, this));         context_.use_certificate_chain_file("server.crt");         context_.use_private_key_file("server.crt", ssl::context::pem);         context_.use_tmp_dh_file("dh2048.pem");          do_accept();     }      ~server() { std::cout << "deleting server\n"; }      std::string get_password() const { return "test"; }      void do_accept() {         session::ptr new_session = boost::make_shared<session>(io_service_, context_);         deadline_.expires_from_now(boost::posix_time::seconds(idle_timeout_seconds));         deadline_.async_wait(boost::bind(&server::handle_deadline, this, bap::error()));         acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, bap::error));     }      void handle_accept(session::ptr new_session, const boost::system::error_code &error) {         std::cout << "server() handle_accept\n";         if (!error) {             std::cout << "server() handle_accept ok\n";             sessions_.push_back(new_session);             new_session->start();             do_accept();         } else {             std::cout << "server() handle_accept error:" << error.message() << std::endl;         }     }      void handle_deadline(boost::system::error_code ec) {         if (!ec) {             io_service_.post([this] {                 // assuming 1 thread runs io_service, no more locking required                 std::cout << "server() shutdown after idle timeout\n";                 acceptor_.cancel();                 acceptor_.close();                  (auto weak_sess : sessions_)                     if (auto sess = weak_sess.lock())                         sess->close();             });         }     }    private:     ba::io_service &io_service_;     tcp::acceptor acceptor_;     ssl::context context_;     ba::deadline_timer deadline_;     std::vector<boost::weak_ptr<session> > sessions_; };  } // namespace bt  void accept_ssl_connection_1() {     ba::io_service svc;     int port = 6767;      std::cout << "port is:" << port << std::endl;      // server     bt::server s(svc, port);      // client     tcp::resolver resolver(svc);     bt::client c(svc, resolver.resolve({"127.0.0.1", std::to_string(port)}), "hello, i'm bob");     bt::client d(svc, resolver.resolve({"127.0.0.1", std::to_string(port)}), "hello, i'm cindy");      svc.run(); }  int main() {     accept_ssl_connection_1(); } 

prints

port is:6767 server() handle_accept server() handle_accept ok session start->handshake handle_connect handle_connect no error handle_connect handle_connect no error server() handle_accept server() handle_accept ok session start->handshake session handle_handshake client handle_handshake session handle_handshake client handle_handshake reply: hello, i'm bob reply: hello, i'm cindy server() shutdown after idle timeout server() handle_accept server() handle_accept error:operation canceled deleting session session::close() session::close() deleting session deleting session deleting server 




wiki

Comments

Popular posts from this blog

Asterisk AGI Python Script to Dialplan does not work -

python - Read npy file directly from S3 StreamingBody -

kotlin - Out-projected type in generic interface prohibits the use of metod with generic parameter -