• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // client.cpp
3 // ~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #include <cstdlib>
12 #include <iostream>
13 #include <boost/bind/bind.hpp>
14 #include <boost/asio.hpp>
15 #include <boost/asio/ssl.hpp>
16 
17 enum { max_length = 1024 };
18 
19 class client
20 {
21 public:
client(boost::asio::io_context & io_context,boost::asio::ssl::context & context,boost::asio::ip::tcp::resolver::results_type endpoints)22   client(boost::asio::io_context& io_context,
23       boost::asio::ssl::context& context,
24       boost::asio::ip::tcp::resolver::results_type endpoints)
25     : socket_(io_context, context)
26   {
27     socket_.set_verify_mode(boost::asio::ssl::verify_peer);
28     socket_.set_verify_callback(
29         boost::bind(&client::verify_certificate, this,
30           boost::placeholders::_1, boost::placeholders::_2));
31 
32     boost::asio::async_connect(socket_.lowest_layer(), endpoints,
33         boost::bind(&client::handle_connect, this,
34           boost::asio::placeholders::error));
35   }
36 
verify_certificate(bool preverified,boost::asio::ssl::verify_context & ctx)37   bool verify_certificate(bool preverified,
38       boost::asio::ssl::verify_context& ctx)
39   {
40     // The verify callback can be used to check whether the certificate that is
41     // being presented is valid for the peer. For example, RFC 2818 describes
42     // the steps involved in doing this for HTTPS. Consult the OpenSSL
43     // documentation for more details. Note that the callback is called once
44     // for each certificate in the certificate chain, starting from the root
45     // certificate authority.
46 
47     // In this example we will simply print the certificate's subject name.
48     char subject_name[256];
49     X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
50     X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
51     std::cout << "Verifying " << subject_name << "\n";
52 
53     return preverified;
54   }
55 
handle_connect(const boost::system::error_code & error)56   void handle_connect(const boost::system::error_code& error)
57   {
58     if (!error)
59     {
60       socket_.async_handshake(boost::asio::ssl::stream_base::client,
61           boost::bind(&client::handle_handshake, this,
62             boost::asio::placeholders::error));
63     }
64     else
65     {
66       std::cout << "Connect failed: " << error.message() << "\n";
67     }
68   }
69 
handle_handshake(const boost::system::error_code & error)70   void handle_handshake(const boost::system::error_code& error)
71   {
72     if (!error)
73     {
74       std::cout << "Enter message: ";
75       std::cin.getline(request_, max_length);
76       size_t request_length = strlen(request_);
77 
78       boost::asio::async_write(socket_,
79           boost::asio::buffer(request_, request_length),
80           boost::bind(&client::handle_write, this,
81             boost::asio::placeholders::error,
82             boost::asio::placeholders::bytes_transferred));
83     }
84     else
85     {
86       std::cout << "Handshake failed: " << error.message() << "\n";
87     }
88   }
89 
handle_write(const boost::system::error_code & error,size_t bytes_transferred)90   void handle_write(const boost::system::error_code& error,
91       size_t bytes_transferred)
92   {
93     if (!error)
94     {
95       boost::asio::async_read(socket_,
96           boost::asio::buffer(reply_, bytes_transferred),
97           boost::bind(&client::handle_read, this,
98             boost::asio::placeholders::error,
99             boost::asio::placeholders::bytes_transferred));
100     }
101     else
102     {
103       std::cout << "Write failed: " << error.message() << "\n";
104     }
105   }
106 
handle_read(const boost::system::error_code & error,size_t bytes_transferred)107   void handle_read(const boost::system::error_code& error,
108       size_t bytes_transferred)
109   {
110     if (!error)
111     {
112       std::cout << "Reply: ";
113       std::cout.write(reply_, bytes_transferred);
114       std::cout << "\n";
115     }
116     else
117     {
118       std::cout << "Read failed: " << error.message() << "\n";
119     }
120   }
121 
122 private:
123   boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
124   char request_[max_length];
125   char reply_[max_length];
126 };
127 
main(int argc,char * argv[])128 int main(int argc, char* argv[])
129 {
130   try
131   {
132     if (argc != 3)
133     {
134       std::cerr << "Usage: client <host> <port>\n";
135       return 1;
136     }
137 
138     boost::asio::io_context io_context;
139 
140     boost::asio::ip::tcp::resolver resolver(io_context);
141     boost::asio::ip::tcp::resolver::results_type endpoints =
142       resolver.resolve(argv[1], argv[2]);
143 
144     boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
145     ctx.load_verify_file("ca.pem");
146 
147     client c(io_context, ctx, endpoints);
148 
149     io_context.run();
150   }
151   catch (std::exception& e)
152   {
153     std::cerr << "Exception: " << e.what() << "\n";
154   }
155 
156   return 0;
157 }
158