asio_ssl_main.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. //
  2. // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #include <string>
  8. #include "protocol_examples_common.h"
  9. #include "esp_event.h"
  10. #include "nvs_flash.h"
  11. #include <cstdlib>
  12. #include <iostream>
  13. #include <chrono>
  14. #include <thread>
  15. #include "asio.hpp"
  16. #include "asio/ssl.hpp"
  17. #include "asio/buffer.hpp"
  18. #include "esp_pthread.h"
  19. extern const unsigned char server_pem_start[] asm("_binary_srv_crt_start");
  20. extern const unsigned char server_pem_end[] asm("_binary_srv_crt_end");
  21. extern const unsigned char cacert_pem_start[] asm("_binary_ca_crt_start");
  22. extern const unsigned char cacert_pem_end[] asm("_binary_ca_crt_end");
  23. extern const unsigned char prvtkey_pem_start[] asm("_binary_server_key_start");
  24. extern const unsigned char prvtkey_pem_end[] asm("_binary_server_key_end");
  25. static const asio::const_buffer cert_chain(cacert_pem_start, cacert_pem_end - cacert_pem_start);
  26. static const asio::const_buffer privkey(prvtkey_pem_start, prvtkey_pem_end - prvtkey_pem_start);
  27. static const asio::const_buffer server_cert(server_pem_start, server_pem_end - server_pem_start);
  28. using asio::ip::tcp;
  29. static const std::size_t max_length = 1024;
  30. class Client {
  31. public:
  32. Client(asio::io_context &io_context,
  33. asio::ssl::context &context,
  34. const tcp::resolver::results_type &endpoints)
  35. : socket_(io_context, context)
  36. {
  37. #if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
  38. socket_.set_verify_mode(asio::ssl::verify_peer);
  39. #else
  40. socket_.set_verify_mode(asio::ssl::verify_none);
  41. #endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
  42. connect(endpoints);
  43. }
  44. private:
  45. void connect(const tcp::resolver::results_type &endpoints)
  46. {
  47. asio::async_connect(socket_.lowest_layer(), endpoints,
  48. [this](const std::error_code & error,
  49. const tcp::endpoint & /*endpoint*/) {
  50. if (!error) {
  51. handshake();
  52. } else {
  53. std::cout << "Connect failed: " << error.message() << "\n";
  54. }
  55. });
  56. }
  57. void handshake()
  58. {
  59. socket_.async_handshake(asio::ssl::stream_base::client,
  60. [this](const std::error_code & error) {
  61. if (!error) {
  62. send_request();
  63. } else {
  64. std::cout << "Handshake failed: " << error.message() << "\n";
  65. }
  66. });
  67. }
  68. void send_request()
  69. {
  70. size_t request_length = std::strlen(request_);
  71. asio::async_write(socket_,
  72. asio::buffer(request_, request_length),
  73. [this](const std::error_code & error, std::size_t length) {
  74. if (!error) {
  75. receive_response(length);
  76. } else {
  77. std::cout << "Write failed: " << error.message() << "\n";
  78. }
  79. });
  80. }
  81. void receive_response(std::size_t length)
  82. {
  83. asio::async_read(socket_,
  84. asio::buffer(reply_, length),
  85. [this](const std::error_code & error, std::size_t length) {
  86. if (!error) {
  87. std::cout << "Reply: ";
  88. std::cout.write(reply_, length);
  89. std::cout << "\n";
  90. } else {
  91. std::cout << "Read failed: " << error.message() << "\n";
  92. }
  93. });
  94. }
  95. asio::ssl::stream<tcp::socket> socket_;
  96. char request_[max_length] = "GET / HTTP/1.1\r\n\r\n";
  97. char reply_[max_length];
  98. };
  99. class Session : public std::enable_shared_from_this<Session> {
  100. public:
  101. Session(tcp::socket socket, asio::ssl::context &context)
  102. : socket_(std::move(socket), context)
  103. {
  104. }
  105. void start()
  106. {
  107. do_handshake();
  108. }
  109. private:
  110. void do_handshake()
  111. {
  112. auto self(shared_from_this());
  113. socket_.async_handshake(asio::ssl::stream_base::server,
  114. [this, self](const std::error_code & error) {
  115. if (!error) {
  116. do_read();
  117. }
  118. });
  119. }
  120. void do_read()
  121. {
  122. auto self(shared_from_this());
  123. socket_.async_read_some(asio::buffer(data_),
  124. [this, self](const std::error_code & ec, std::size_t length) {
  125. if (!ec) {
  126. std::cout << "Server received: ";
  127. std::cout.write(data_, length);
  128. std::cout << std::endl;
  129. do_write(length);
  130. }
  131. });
  132. }
  133. void do_write(std::size_t length)
  134. {
  135. auto self(shared_from_this());
  136. asio::async_write(socket_, asio::buffer(data_, length),
  137. [this, self](const std::error_code & ec,
  138. std::size_t /*length*/) {
  139. if (!ec) {
  140. do_read();
  141. }
  142. });
  143. }
  144. asio::ssl::stream<tcp::socket> socket_;
  145. char data_[max_length];
  146. };
  147. class Server {
  148. public:
  149. Server(asio::io_context &io_context, unsigned short port)
  150. : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
  151. context_(asio::ssl::context::tls_server)
  152. {
  153. context_.set_options(
  154. asio::ssl::context::default_workarounds
  155. | asio::ssl::context::no_sslv2);
  156. context_.use_certificate_chain(server_cert);
  157. context_.use_private_key(privkey, asio::ssl::context::pem);
  158. do_accept();
  159. }
  160. private:
  161. void do_accept()
  162. {
  163. acceptor_.async_accept(
  164. [this](const std::error_code & error, tcp::socket socket) {
  165. if (!error) {
  166. std::make_shared<Session>(std::move(socket), context_)->start();
  167. }
  168. do_accept();
  169. });
  170. }
  171. tcp::acceptor acceptor_;
  172. asio::ssl::context context_;
  173. };
  174. void set_thread_config(const char *name, int stack, int prio)
  175. {
  176. auto cfg = esp_pthread_get_default_config();
  177. cfg.thread_name = name;
  178. cfg.stack_size = stack;
  179. cfg.prio = prio;
  180. esp_pthread_set_cfg(&cfg);
  181. }
  182. void ssl_server_thread()
  183. {
  184. asio::io_context io_context;
  185. Server s(io_context, 443);
  186. io_context.run();
  187. }
  188. void ssl_client_thread()
  189. {
  190. asio::io_context io_context;
  191. tcp::resolver resolver(io_context);
  192. std::string server_ip = CONFIG_EXAMPLE_SERVER_NAME;
  193. std::string server_port = CONFIG_EXAMPLE_PORT;
  194. auto endpoints = resolver.resolve(server_ip, server_port);
  195. asio::ssl::context ctx(asio::ssl::context::tls_client);
  196. #if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
  197. ctx.add_certificate_authority(cert_chain);
  198. #endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
  199. Client c(io_context, ctx, endpoints);
  200. io_context.run();
  201. }
  202. extern "C" void app_main(void)
  203. {
  204. ESP_ERROR_CHECK(nvs_flash_init());
  205. esp_netif_init();
  206. ESP_ERROR_CHECK(esp_event_loop_create_default());
  207. /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
  208. * Read "Establishing Wi-Fi or Ethernet Connection" section in
  209. * examples/protocols/README.md for more information about this function.
  210. */
  211. ESP_ERROR_CHECK(example_connect());
  212. /* This helper function configures blocking UART I/O */
  213. ESP_ERROR_CHECK(example_configure_stdin_stdout());
  214. std::vector<std::thread> work_threads;
  215. #if CONFIG_EXAMPLE_SERVER
  216. set_thread_config("Server", 16 * 1024, 5);
  217. work_threads.emplace_back(std::thread(ssl_server_thread));
  218. std::this_thread::sleep_for(std::chrono::seconds(1));
  219. #endif // CONFIG_EXAMPLE_SERVER
  220. #if CONFIG_EXAMPLE_CLIENT
  221. set_thread_config("Client", 16 * 1024, 5);
  222. work_threads.emplace_back(ssl_client_thread);
  223. #endif // CONFIG_EXAMPLE_CLIENT
  224. for (auto &t : work_threads) {
  225. t.join();
  226. }
  227. }