Frage

I tried to solve my Problem for a few days now and just can't get behind it. I try to do an SSL Connection with the Boost::Asio Library and OpenSSL. There is an Example Code, how to do this: http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp03/ssl/client.cpp

It builds and runs fine and even the verifying seems to work, but when I send a simple request, nothing happens for quite a long time, and then i get an error Message: "Read Failed: short read". I guess, I'm just doing the request line itself wrong, but i can't figure out how it has to be. I tried both the following lines:

GET / HTTP/1.1<CR><LF>Host: www.google.de<CR><LF>Connection: close<CR><LF><CR><LF>

GET / HTTP/1.1\r\nHost: www.google.de\r\nConnection: close\r\n\r\n

I can't post images, because this is my first Post, but you can find the cmd-Window here: http://imgur.com/UOeENm5

Thanks in Advance!

Update: I got it to work! :D Big thanks to sehe, i just had to change line 77 in your code from:

size_t request_length = strlen(request_);

to:

 size_t request_length = strlen(raw);

Now it works just fine, thx for your help :)

I still don't understand, why the boost example code isn't working as intended for me..

War es hilfreich?

Lösung

Hah!

I've found more time and looked at it again. My code here (http://coliru.stacked-crooked.com/a/2042c3d24a16c6ac) was already almost correct. Except for one really simple bug:

size_t request_length = strlen(request_);

Looks innocuous, no? Except... request_ was uninitialized at this point. It needed to be (in context):

  static char const raw[] = "GET / HTTP/1.1\r\nHost: www.google.de\r\nConnection: close\r\n\r\n";

  static_assert(sizeof(raw)<=sizeof(request_), "too large");

  size_t request_length = strlen(raw);
  std::copy(raw, raw+request_length, request_);

Of course, this problem did not exist in the official Boost sample you linked to (as it reads directly into the request buffer with std::cin.getline). However, like me, you might have made the same beginner's mistake.

Hope this helps.

Attached is a fully working demo that you van even switch to use HTTPS or not using preprocessor directive:

#define SEHE_USE_SSL

Note also, I used

ctx.set_default_verify_paths();

so you can use the default root authority certificates registered on your machine (if your openssl installation has it).

Full Code

#define SEHE_USE_SSL
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING

#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

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
#ifdef SEHE_USE_SSL
            , context)
  {
    socket_.set_verify_mode(boost::asio::ssl::verify_peer);
    socket_.set_verify_callback(
        boost::bind(&client::verify_certificate, this, _1, _2));
#else
            )
  {
      (void) context;
#endif

    boost::asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
        boost::bind(&client::handle_connect, this,
          boost::asio::placeholders::error));
  }

  bool verify_certificate(bool preverified,
      boost::asio::ssl::verify_context& ctx)
  {
      // The verify callback can be used to check whether the certificate that is
      // being presented is valid for the peer. For example, RFC 2818 describes
      // the steps involved in doing this for HTTPS. Consult the OpenSSL
      // documentation for more details. Note that the callback is called once
      // for each certificate in the certificate chain, starting from the root
      // certificate authority.

      // In this example we will simply print the certificate's subject name.
      char subject_name[256];
      X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
      X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
      std::cout << "Verifying " << subject_name << "\n";

      return true || preverified;
  }

  void handle_connect(const boost::system::error_code& error)
  {
#ifdef SEHE_USE_SSL
      if (!error)
      {
          socket_.async_handshake(boost::asio::ssl::stream_base::client,
                  boost::bind(&client::handle_handshake, this,
                      boost::asio::placeholders::error));
      }
      else
      {
          std::cout << "Connect failed: " << error.message() << "\n";
      }
#else
      handle_handshake(error);
#endif
  }

  void handle_handshake(const boost::system::error_code& error)
  {
      if (!error)
      {
          std::cout << "Enter message: ";
          static char const raw[] = "GET / HTTP/1.1\r\nHost: www.google.de\r\nConnection: close\r\n\r\n";

          static_assert(sizeof(raw)<=sizeof(request_), "too large");

          size_t request_length = strlen(raw);
          std::copy(raw, raw+request_length, request_);

          {
              // used this for debugging:
              std::ostream hexos(std::cout.rdbuf());
              for(auto it = raw; it != raw+request_length; ++it)
                  hexos << std::hex << std::setw(2) << std::setfill('0') << std::showbase << ((short unsigned) *it) << " ";
              std::cout << "\n";
          }

          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.message() << "\n";
      }
  }

  void handle_write(const boost::system::error_code& error,
      size_t /*bytes_transferred*/)
  {
      if (!error)
      {
          std::cout << "starting read loop\n";
          boost::asio::async_read_until(socket_,
                  //boost::asio::buffer(reply_, sizeof(reply_)),
                  reply_, '\n',
                  boost::bind(&client::handle_read, this,
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::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: " << &reply_ << "\n";
      }
      else
      {
          std::cout << "Read failed: " << error.message() << "\n";
      }
  }

private:
#ifdef SEHE_USE_SSL
  boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
#else
  boost::asio::ip::tcp::socket socket_;
#endif
  char request_[1024];
  //char reply_  [1024];
  boost::asio::streambuf reply_;
};

int main(int argc, char* argv[])
{
    try
    {
        if (argc != 3)
        {
            std::cerr << "Usage: client <host> <port>\n";
            return 1;
        }

        boost::asio::io_service io_service;

        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query(argv[1], argv[2]);
        boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);

        boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
        ctx.set_default_verify_paths();

        client c(io_service, ctx, iterator);

        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top