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