| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- //
- // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- #include <string>
- #include "protocol_examples_common.h"
- #include "esp_event.h"
- #include "nvs_flash.h"
- #include <cstdlib>
- #include <iostream>
- #include <chrono>
- #include <thread>
- #include "asio.hpp"
- #include "asio/ssl.hpp"
- #include "asio/buffer.hpp"
- #include "esp_pthread.h"
- extern const unsigned char server_pem_start[] asm("_binary_srv_crt_start");
- extern const unsigned char server_pem_end[] asm("_binary_srv_crt_end");
- extern const unsigned char cacert_pem_start[] asm("_binary_ca_crt_start");
- extern const unsigned char cacert_pem_end[] asm("_binary_ca_crt_end");
- extern const unsigned char prvtkey_pem_start[] asm("_binary_server_key_start");
- extern const unsigned char prvtkey_pem_end[] asm("_binary_server_key_end");
- static const asio::const_buffer cert_chain(cacert_pem_start, cacert_pem_end - cacert_pem_start);
- static const asio::const_buffer privkey(prvtkey_pem_start, prvtkey_pem_end - prvtkey_pem_start);
- static const asio::const_buffer server_cert(server_pem_start, server_pem_end - server_pem_start);
- using asio::ip::tcp;
- static const std::size_t max_length = 1024;
- class Client {
- public:
- Client(asio::io_context &io_context,
- asio::ssl::context &context,
- const tcp::resolver::results_type &endpoints)
- : socket_(io_context, context)
- {
- #if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
- socket_.set_verify_mode(asio::ssl::verify_peer);
- #else
- socket_.set_verify_mode(asio::ssl::verify_none);
- #endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
- connect(endpoints);
- }
- private:
- void connect(const tcp::resolver::results_type &endpoints)
- {
- asio::async_connect(socket_.lowest_layer(), endpoints,
- [this](const std::error_code & error,
- const tcp::endpoint & /*endpoint*/) {
- if (!error) {
- handshake();
- } else {
- std::cout << "Connect failed: " << error.message() << "\n";
- }
- });
- }
- void handshake()
- {
- socket_.async_handshake(asio::ssl::stream_base::client,
- [this](const std::error_code & error) {
- if (!error) {
- send_request();
- } else {
- std::cout << "Handshake failed: " << error.message() << "\n";
- }
- });
- }
- void send_request()
- {
- size_t request_length = std::strlen(request_);
- asio::async_write(socket_,
- asio::buffer(request_, request_length),
- [this](const std::error_code & error, std::size_t length) {
- if (!error) {
- receive_response(length);
- } else {
- std::cout << "Write failed: " << error.message() << "\n";
- }
- });
- }
- void receive_response(std::size_t length)
- {
- asio::async_read(socket_,
- asio::buffer(reply_, length),
- [this](const std::error_code & error, std::size_t length) {
- if (!error) {
- std::cout << "Reply: ";
- std::cout.write(reply_, length);
- std::cout << "\n";
- } else {
- std::cout << "Read failed: " << error.message() << "\n";
- }
- });
- }
- asio::ssl::stream<tcp::socket> socket_;
- char request_[max_length] = "GET / HTTP/1.1\r\n\r\n";
- char reply_[max_length];
- };
- class Session : public std::enable_shared_from_this<Session> {
- public:
- Session(tcp::socket socket, asio::ssl::context &context)
- : socket_(std::move(socket), context)
- {
- }
- void start()
- {
- do_handshake();
- }
- private:
- void do_handshake()
- {
- auto self(shared_from_this());
- socket_.async_handshake(asio::ssl::stream_base::server,
- [this, self](const std::error_code & error) {
- if (!error) {
- do_read();
- }
- });
- }
- void do_read()
- {
- auto self(shared_from_this());
- socket_.async_read_some(asio::buffer(data_),
- [this, self](const std::error_code & ec, std::size_t length) {
- if (!ec) {
- std::cout << "Server received: ";
- std::cout.write(data_, length);
- std::cout << std::endl;
- do_write(length);
- }
- });
- }
- void do_write(std::size_t length)
- {
- auto self(shared_from_this());
- asio::async_write(socket_, asio::buffer(data_, length),
- [this, self](const std::error_code & ec,
- std::size_t /*length*/) {
- if (!ec) {
- do_read();
- }
- });
- }
- asio::ssl::stream<tcp::socket> socket_;
- char data_[max_length];
- };
- class Server {
- public:
- Server(asio::io_context &io_context, unsigned short port)
- : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
- context_(asio::ssl::context::tls_server)
- {
- context_.set_options(
- asio::ssl::context::default_workarounds
- | asio::ssl::context::no_sslv2);
- context_.use_certificate_chain(server_cert);
- context_.use_private_key(privkey, asio::ssl::context::pem);
- do_accept();
- }
- private:
- void do_accept()
- {
- acceptor_.async_accept(
- [this](const std::error_code & error, tcp::socket socket) {
- if (!error) {
- std::make_shared<Session>(std::move(socket), context_)->start();
- }
- do_accept();
- });
- }
- tcp::acceptor acceptor_;
- asio::ssl::context context_;
- };
- void set_thread_config(const char *name, int stack, int prio)
- {
- auto cfg = esp_pthread_get_default_config();
- cfg.thread_name = name;
- cfg.stack_size = stack;
- cfg.prio = prio;
- esp_pthread_set_cfg(&cfg);
- }
- void ssl_server_thread()
- {
- asio::io_context io_context;
- Server s(io_context, 443);
- io_context.run();
- }
- void ssl_client_thread()
- {
- asio::io_context io_context;
- tcp::resolver resolver(io_context);
- std::string server_ip = CONFIG_EXAMPLE_SERVER_NAME;
- std::string server_port = CONFIG_EXAMPLE_PORT;
- auto endpoints = resolver.resolve(server_ip, server_port);
- asio::ssl::context ctx(asio::ssl::context::tls_client);
- #if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
- ctx.add_certificate_authority(cert_chain);
- #endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
- Client c(io_context, ctx, endpoints);
- io_context.run();
- }
- extern "C" void app_main(void)
- {
- ESP_ERROR_CHECK(nvs_flash_init());
- esp_netif_init();
- ESP_ERROR_CHECK(esp_event_loop_create_default());
- /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
- * Read "Establishing Wi-Fi or Ethernet Connection" section in
- * examples/protocols/README.md for more information about this function.
- */
- ESP_ERROR_CHECK(example_connect());
- /* This helper function configures blocking UART I/O */
- ESP_ERROR_CHECK(example_configure_stdin_stdout());
- std::vector<std::thread> work_threads;
- #if CONFIG_EXAMPLE_SERVER
- set_thread_config("Server", 16 * 1024, 5);
- work_threads.emplace_back(std::thread(ssl_server_thread));
- std::this_thread::sleep_for(std::chrono::seconds(1));
- #endif // CONFIG_EXAMPLE_SERVER
- #if CONFIG_EXAMPLE_CLIENT
- set_thread_config("Client", 16 * 1024, 5);
- work_threads.emplace_back(ssl_client_thread);
- #endif // CONFIG_EXAMPLE_CLIENT
- for (auto &t : work_threads) {
- t.join();
- }
- }
|