asio_ssl_main.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. const asio::const_buffer cert_chain(cacert_pem_start, cacert_pem_end - cacert_pem_start);
  26. const asio::const_buffer privkey(prvtkey_pem_start, prvtkey_pem_end - prvtkey_pem_start);
  27. const asio::const_buffer server_cert(server_pem_start, server_pem_end - server_pem_start);
  28. using asio::ip::tcp;
  29. enum { 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. {
  51. if (!error)
  52. {
  53. handshake();
  54. }
  55. else
  56. {
  57. std::cout << "Connect failed: " << error.message() << "\n";
  58. }
  59. });
  60. }
  61. void handshake()
  62. {
  63. socket_.async_handshake(asio::ssl::stream_base::client,
  64. [this](const std::error_code& error)
  65. {
  66. if (!error)
  67. {
  68. send_request();
  69. }
  70. else
  71. {
  72. std::cout << "Handshake failed: " << error.message() << "\n";
  73. }
  74. });
  75. }
  76. void send_request()
  77. {
  78. size_t request_length = std::strlen(request_);
  79. asio::async_write(socket_,
  80. asio::buffer(request_, request_length),
  81. [this](const std::error_code& error, std::size_t length)
  82. {
  83. if (!error)
  84. {
  85. receive_response(length);
  86. }
  87. else
  88. {
  89. std::cout << "Write failed: " << error.message() << "\n";
  90. }
  91. });
  92. }
  93. void receive_response(std::size_t length)
  94. {
  95. asio::async_read(socket_,
  96. asio::buffer(reply_, length),
  97. [this](const std::error_code& error, std::size_t length)
  98. {
  99. if (!error)
  100. {
  101. std::cout << "Reply: ";
  102. std::cout.write(reply_, length);
  103. std::cout << "\n";
  104. }
  105. else
  106. {
  107. std::cout << "Read failed: " << error.message() << "\n";
  108. }
  109. });
  110. }
  111. asio::ssl::stream<tcp::socket> socket_;
  112. char request_[max_length] = "GET / HTTP/1.1\r\n\r\n";
  113. char reply_[max_length];
  114. };
  115. class Session : public std::enable_shared_from_this<Session> {
  116. public:
  117. Session(tcp::socket socket, asio::ssl::context& context)
  118. : socket_(std::move(socket), context)
  119. {
  120. }
  121. void start()
  122. {
  123. do_handshake();
  124. }
  125. private:
  126. void do_handshake()
  127. {
  128. auto self(shared_from_this());
  129. socket_.async_handshake(asio::ssl::stream_base::server,
  130. [this, self](const std::error_code& error)
  131. {
  132. if (!error)
  133. {
  134. do_read();
  135. }
  136. });
  137. }
  138. void do_read()
  139. {
  140. auto self(shared_from_this());
  141. socket_.async_read_some(asio::buffer(data_),
  142. [this, self](const std::error_code& ec, std::size_t length)
  143. {
  144. if (!ec)
  145. {
  146. data_[length] = 0;
  147. std::cout << "Server received: " << data_ << std::endl;
  148. do_write(length);
  149. }
  150. });
  151. }
  152. void do_write(std::size_t length)
  153. {
  154. auto self(shared_from_this());
  155. asio::async_write(socket_, asio::buffer(data_, length),
  156. [this, self](const std::error_code& ec,
  157. std::size_t /*length*/)
  158. {
  159. if (!ec)
  160. {
  161. do_read();
  162. }
  163. });
  164. }
  165. asio::ssl::stream<tcp::socket> socket_;
  166. char data_[max_length];
  167. };
  168. class Server {
  169. public:
  170. Server(asio::io_context& io_context, unsigned short port)
  171. : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
  172. context_(asio::ssl::context::tls_server)
  173. {
  174. context_.set_options(
  175. asio::ssl::context::default_workarounds
  176. | asio::ssl::context::no_sslv2);
  177. context_.use_certificate_chain(server_cert);
  178. context_.use_private_key(privkey, asio::ssl::context::pem);
  179. do_accept();
  180. }
  181. private:
  182. void do_accept()
  183. {
  184. acceptor_.async_accept(
  185. [this](const std::error_code& error, tcp::socket socket)
  186. {
  187. if (!error)
  188. {
  189. std::make_shared<Session>(std::move(socket), context_)->start();
  190. }
  191. do_accept();
  192. });
  193. }
  194. tcp::acceptor acceptor_;
  195. asio::ssl::context context_;
  196. };
  197. void set_thread_config(const char *name, int stack, int prio)
  198. {
  199. auto cfg = esp_pthread_get_default_config();
  200. cfg.thread_name = name;
  201. cfg.stack_size = stack;
  202. cfg.prio = prio;
  203. esp_pthread_set_cfg(&cfg);
  204. }
  205. void ssl_server_thread()
  206. {
  207. asio::io_context io_context;
  208. Server s(io_context, 443);
  209. io_context.run();
  210. }
  211. void ssl_client_thread()
  212. {
  213. asio::io_context io_context;
  214. tcp::resolver resolver(io_context);
  215. std::string server_ip = CONFIG_EXAMPLE_SERVER_NAME;
  216. std::string server_port = CONFIG_EXAMPLE_PORT;
  217. auto endpoints = resolver.resolve(server_ip, server_port);
  218. asio::ssl::context ctx(asio::ssl::context::tls_client);
  219. #if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
  220. ctx.add_certificate_authority(cert_chain);
  221. #endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
  222. Client c(io_context, ctx, endpoints);
  223. io_context.run();
  224. }
  225. extern "C" void app_main(void)
  226. {
  227. ESP_ERROR_CHECK(nvs_flash_init());
  228. esp_netif_init();
  229. ESP_ERROR_CHECK(esp_event_loop_create_default());
  230. /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
  231. * Read "Establishing Wi-Fi or Ethernet Connection" section in
  232. * examples/protocols/README.md for more information about this function.
  233. */
  234. ESP_ERROR_CHECK(example_connect());
  235. /* This helper function configures blocking UART I/O */
  236. ESP_ERROR_CHECK(example_configure_stdin_stdout());
  237. std::vector<std::thread> work_threads;
  238. #if CONFIG_EXAMPLE_SERVER
  239. set_thread_config("Server", 16 * 1024, 5);
  240. work_threads.emplace_back(std::thread(ssl_server_thread));
  241. std::this_thread::sleep_for(std::chrono::seconds(1));
  242. #endif // CONFIG_EXAMPLE_SERVER
  243. #if CONFIG_EXAMPLE_CLIENT
  244. set_thread_config("Client", 16 * 1024, 5);
  245. work_threads.emplace_back(ssl_client_thread);
  246. #endif // CONFIG_EXAMPLE_CLIENT
  247. for (auto & t : work_threads) {
  248. t.join();
  249. }
  250. }