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