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