• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 5371398685

25 Jun 2023 07:16PM UTC coverage: 91.742% (+0.008%) from 91.734%
5371398685

push

github

randombit
Merge GH #3600 Further clang-tidy fixes

78182 of 85219 relevant lines covered (91.74%)

12458781.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.31
/src/cli/tls_proxy.cpp
1
/*
2
* TLS Server Proxy
3
* (C) 2014,2015,2019 Jack Lloyd
4
* (C) 2016 Matthias Gierlings
5
* (C) 2023 René Meusel, Rohde & Schwarz Cybersecurity
6
*
7
* Botan is released under the Simplified BSD License (see license.txt)
8
*/
9

10
#include "cli.h"
11

12
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
13

14
   #include <atomic>
15
   #include <iostream>
16
   #include <string>
17
   #include <thread>
18
   #include <utility>
19
   #include <vector>
20

21
   #include <botan/internal/os_utils.h>
22
   #include <boost/asio.hpp>
23
   #include <boost/bind.hpp>
24

25
   #include <botan/hex.h>
26
   #include <botan/pkcs8.h>
27
   #include <botan/rng.h>
28
   #include <botan/tls_server.h>
29
   #include <botan/tls_session_manager_memory.h>
30
   #include <botan/x509cert.h>
31

32
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
33
      #include <botan/tls_session_manager_sqlite.h>
34
   #endif
35

36
   #include "tls_helpers.h"
37

38
namespace Botan_CLI {
39

40
namespace {
41

42
using boost::asio::ip::tcp;
43

44
template <typename T>
45
boost::asio::io_context& get_io_service(T& s) {
4✔
46
   #if BOOST_VERSION >= 107000
47
   //NOLINTNEXTLINE(*-type-static-cast-downcast)
48
   return static_cast<boost::asio::io_context&>((s).get_executor().context());
8✔
49
   #else
50
   return s.get_io_service();
51
   #endif
52
}
53

54
void log_exception(const char* where, const std::exception& e) {
×
55
   std::cout << where << ' ' << e.what() << std::endl;
×
56
}
×
57

58
void log_error(const char* where, const boost::system::error_code& error) {
8✔
59
   std::cout << where << ' ' << error.message() << std::endl;
8✔
60
}
8✔
61

62
void log_error(const char* msg) {
×
63
   std::cout << msg << std::endl;
×
64
}
65

66
void log_binary_message(const char* where, const uint8_t buf[], size_t buf_len) {
67
   BOTAN_UNUSED(where, buf, buf_len);
68
   //std::cout << where << ' ' << Botan::hex_encode(buf, buf_len) << std::endl;
69
}
70

71
void log_text_message(const char* where, const uint8_t buf[], size_t buf_len) {
72
   BOTAN_UNUSED(where, buf, buf_len);
73
   //const char* c = reinterpret_cast<const char*>(buf);
74
   //std::cout << where << ' ' << std::string(c, c + buf_len)  << std::endl;
75
}
76

77
class ServerStatus {
78
   public:
79
      ServerStatus(size_t max_clients) : m_max_clients(max_clients), m_clients_serviced(0) {}
1✔
80

81
      bool should_exit() const {
4✔
82
         if(m_max_clients == 0) {
4✔
83
            return false;
84
         }
85

86
         return clients_serviced() >= m_max_clients;
4✔
87
      }
88

89
      void client_serviced() { m_clients_serviced++; }
4✔
90

91
      size_t clients_serviced() const { return m_clients_serviced.load(); }
4✔
92

93
   private:
94
      size_t m_max_clients;
95
      std::atomic<size_t> m_clients_serviced;
96
};
97

98
class tls_proxy_session final : public std::enable_shared_from_this<tls_proxy_session>,
99
                                public Botan::TLS::Callbacks {
100
   public:
101
      enum { readbuf_size = 17 * 1024 };
102

103
      typedef std::shared_ptr<tls_proxy_session> pointer;
104

105
      static pointer create(boost::asio::io_service& io,
4✔
106
                            const std::shared_ptr<Botan::TLS::Session_Manager>& session_manager,
107
                            const std::shared_ptr<Botan::Credentials_Manager>& credentials,
108
                            const std::shared_ptr<Botan::TLS::Policy>& policy,
109
                            const tcp::resolver::iterator& endpoints) {
110
         auto session = std::make_shared<tls_proxy_session>(io, endpoints);
4✔
111

112
         // Defer the setup of the TLS server to make use of
113
         // shared_from_this() which wouldn't work in the c'tor.
114
         session->setup(session_manager, credentials, policy);
4✔
115

116
         return session;
4✔
117
      }
×
118

119
      tcp::socket& client_socket() { return m_client_socket; }
1✔
120

121
      void start() {
4✔
122
         m_c2p.resize(readbuf_size);
4✔
123
         client_read(boost::system::error_code(), 0);  // start read loop
4✔
124
      }
4✔
125

126
      void stop() {
8✔
127
         if(m_is_closed == false) {
8✔
128
            /*
129
            Don't need to talk to the server anymore
130
            Client socket is closed during write callback
131
            */
132
            m_server_socket.close();
4✔
133
            m_tls->close();
4✔
134

135
            // Need to explicitly destroy the TLS::Server object to break the
136
            // circular ownership of shared_from_this() and the shared_ptr of
137
            // this kept inside the TLS::Channel.
138
            m_tls.reset();
4✔
139
            m_is_closed = true;
4✔
140
         }
141
      }
8✔
142

143
      tls_proxy_session(boost::asio::io_service& io, tcp::resolver::iterator endpoints) :
4✔
144
            m_strand(io),
4✔
145
            m_server_endpoints(std::move(endpoints)),
4✔
146
            m_client_socket(io),
4✔
147
            m_server_socket(io),
8✔
148
            m_rng(cli_make_rng()) {}
12✔
149

150
   private:
151
      void setup(const std::shared_ptr<Botan::TLS::Session_Manager>& session_manager,
4✔
152
                 const std::shared_ptr<Botan::Credentials_Manager>& credentials,
153
                 const std::shared_ptr<Botan::TLS::Policy>& policy) {
154
         m_tls = std::make_unique<Botan::TLS::Server>(shared_from_this(), session_manager, credentials, policy, m_rng);
4✔
155
      }
4✔
156

157
      void client_read(const boost::system::error_code& error, size_t bytes_transferred) {
18✔
158
         if(error) {
18✔
159
            log_error("Read failed", error);
4✔
160
            stop();
4✔
161
            return;
4✔
162
         }
163

164
         if(m_is_closed) {
14✔
165
            log_error("Received client data after close");
×
166
            return;
×
167
         }
168

169
         try {
14✔
170
            if(!m_tls->is_active()) {
14✔
171
               log_binary_message("From client", &m_c2p[0], bytes_transferred);
172
            }
173
            m_tls->received_data(&m_c2p[0], bytes_transferred);
14✔
174
         } catch(Botan::Exception& e) {
×
175
            log_exception("TLS connection failed", e);
×
176
            stop();
×
177
            return;
×
178
         }
×
179

180
         m_client_socket.async_read_some(boost::asio::buffer(&m_c2p[0], m_c2p.size()),
28✔
181
                                         m_strand.wrap(boost::bind(&tls_proxy_session::client_read,
28✔
182
                                                                   shared_from_this(),
28✔
183
                                                                   boost::asio::placeholders::error,
184
                                                                   boost::asio::placeholders::bytes_transferred)));
185
      }
186

187
      void handle_client_write_completion(const boost::system::error_code& error) {
24✔
188
         if(error) {
24✔
189
            log_error("Client write", error);
×
190
            stop();
×
191
            return;
×
192
         }
193

194
         m_p2c.clear();
24✔
195

196
         if(m_p2c_pending.empty() && (!m_tls || m_tls->is_closed())) {
24✔
197
            m_client_socket.close();
4✔
198
         }
199
         tls_emit_data({});  // initiate another write if needed
24✔
200
      }
201

202
      void handle_server_write_completion(const boost::system::error_code& error) {
4✔
203
         if(error) {
4✔
204
            log_error("Server write", error);
×
205
            stop();
×
206
            return;
×
207
         }
208

209
         m_p2s.clear();
4✔
210
         proxy_write_to_server({});  // initiate another write if needed
4✔
211
      }
212

213
      void tls_record_received(uint64_t /*rec_no*/, std::span<const uint8_t> buf) override {
4✔
214
         // Immediately bounce message to server
215
         proxy_write_to_server(buf);
4✔
216
      }
4✔
217

218
      void tls_emit_data(std::span<const uint8_t> buf) override {
53✔
219
         if(!buf.empty()) {
53✔
220
            m_p2c_pending.insert(m_p2c_pending.end(), buf.begin(), buf.end());
29✔
221
         }
222

223
         // no write now active and we still have output pending
224
         if(m_p2c.empty() && !m_p2c_pending.empty()) {
53✔
225
            std::swap(m_p2c_pending, m_p2c);
24✔
226

227
            log_binary_message("To Client", &m_p2c[0], m_p2c.size());
24✔
228

229
            boost::asio::async_write(m_client_socket,
24✔
230
                                     boost::asio::buffer(&m_p2c[0], m_p2c.size()),
24✔
231
                                     m_strand.wrap(boost::bind(&tls_proxy_session::handle_client_write_completion,
48✔
232
                                                               shared_from_this(),
48✔
233
                                                               boost::asio::placeholders::error)));
234
         }
235
      }
53✔
236

237
      void proxy_write_to_server(std::span<const uint8_t> buf) {
12✔
238
         if(!buf.empty()) {
12✔
239
            m_p2s_pending.insert(m_p2s_pending.end(), buf.begin(), buf.end());
4✔
240
         }
241

242
         // no write now active and we still have output pending
243
         if(m_p2s.empty() && !m_p2s_pending.empty()) {
12✔
244
            std::swap(m_p2s_pending, m_p2s);
4✔
245

246
            log_text_message("To Server", &m_p2s[0], m_p2s.size());
4✔
247

248
            boost::asio::async_write(m_server_socket,
4✔
249
                                     boost::asio::buffer(&m_p2s[0], m_p2s.size()),
4✔
250
                                     m_strand.wrap(boost::bind(&tls_proxy_session::handle_server_write_completion,
8✔
251
                                                               shared_from_this(),
8✔
252
                                                               boost::asio::placeholders::error)));
253
         }
254
      }
12✔
255

256
      void server_read(const boost::system::error_code& error, size_t bytes_transferred) {
15✔
257
         if(error) {
15✔
258
            log_error("Server read failed", error);
4✔
259
            stop();
4✔
260
            return;
4✔
261
         }
262

263
         try {
11✔
264
            if(bytes_transferred) {
11✔
265
               log_text_message("Server to client", &m_s2p[0], m_s2p.size());
7✔
266
               log_binary_message("Server to client", &m_s2p[0], m_s2p.size());
7✔
267
               m_tls->send(&m_s2p[0], bytes_transferred);
7✔
268
            }
269
         } catch(Botan::Exception& e) {
×
270
            log_exception("TLS connection failed", e);
×
271
            stop();
×
272
            return;
×
273
         }
×
274

275
         m_s2p.resize(readbuf_size);
11✔
276

277
         m_server_socket.async_read_some(boost::asio::buffer(&m_s2p[0], m_s2p.size()),
22✔
278
                                         m_strand.wrap(boost::bind(&tls_proxy_session::server_read,
22✔
279
                                                                   shared_from_this(),
22✔
280
                                                                   boost::asio::placeholders::error,
281
                                                                   boost::asio::placeholders::bytes_transferred)));
282
      }
283

284
      void tls_session_activated() override {
4✔
285
         auto onConnect = [this](boost::system::error_code ec, const tcp::resolver::iterator& /*endpoint*/) {
16✔
286
            if(ec) {
4✔
287
               log_error("Server connection", ec);
×
288
               return;
×
289
            }
290
            server_read(boost::system::error_code(), 0);  // start read loop
4✔
291
            proxy_write_to_server({});
4✔
292
         };
4✔
293
         async_connect(m_server_socket, m_server_endpoints, onConnect);
4✔
294
      }
4✔
295

296
      void tls_session_established(const Botan::TLS::Session_Summary& session) override {
4✔
297
         m_hostname = session.server_info().hostname();
4✔
298
      }
4✔
299

300
      void tls_alert(Botan::TLS::Alert alert) override {
×
301
         if(alert.type() == Botan::TLS::Alert::CloseNotify) {
×
302
            m_tls->close();
×
303
            return;
×
304
         }
305
      }
306

307
      boost::asio::io_service::strand m_strand;
308

309
      tcp::resolver::iterator m_server_endpoints;
310

311
      tcp::socket m_client_socket;
312
      tcp::socket m_server_socket;
313

314
      std::shared_ptr<Botan::RandomNumberGenerator> m_rng;
315
      std::unique_ptr<Botan::TLS::Server> m_tls;
316
      std::string m_hostname;
317

318
      std::vector<uint8_t> m_c2p;
319
      std::vector<uint8_t> m_p2c;
320
      std::vector<uint8_t> m_p2c_pending;
321

322
      std::vector<uint8_t> m_s2p;
323
      std::vector<uint8_t> m_p2s;
324
      std::vector<uint8_t> m_p2s_pending;
325

326
      bool m_is_closed = false;
327
};
328

329
class tls_proxy_server final {
330
   public:
331
      typedef tls_proxy_session session;
332

333
      tls_proxy_server(boost::asio::io_service& io,
1✔
334
                       unsigned short port,
335
                       tcp::resolver::iterator endpoints,
336
                       std::shared_ptr<Botan::Credentials_Manager> creds,
337
                       std::shared_ptr<Botan::TLS::Policy> policy,
338
                       std::shared_ptr<Botan::TLS::Session_Manager> session_mgr,
339
                       size_t max_clients) :
1✔
340
            m_acceptor(io, tcp::endpoint(tcp::v4(), port)),
1✔
341
            m_server_endpoints(std::move(endpoints)),
1✔
342
            m_creds(std::move(creds)),
1✔
343
            m_policy(std::move(policy)),
1✔
344
            m_session_manager(std::move(session_mgr)),
1✔
345
            m_status(max_clients) {
1✔
346
         session::pointer new_session = make_session();
1✔
347

348
         m_acceptor.async_accept(
2✔
349
            new_session->client_socket(),
1✔
350
            boost::bind(&tls_proxy_server::handle_accept, this, new_session, boost::asio::placeholders::error));
3✔
351
      }
1✔
352

353
   private:
354
      session::pointer make_session() {
4✔
355
         return session::create(get_io_service(m_acceptor), m_session_manager, m_creds, m_policy, m_server_endpoints);
4✔
356
      }
357

358
      void serve_one_session() {
3✔
359
         session::pointer new_session = make_session();
3✔
360

361
         m_acceptor.async_accept(
6✔
362
            new_session->client_socket(),
3✔
363
            boost::bind(&tls_proxy_server::handle_accept, this, new_session, boost::asio::placeholders::error));
9✔
364
      }
3✔
365

366
      void handle_accept(const session::pointer& new_session, const boost::system::error_code& error) {
4✔
367
         if(!error) {
4✔
368
            new_session->start();
4✔
369
            m_status.client_serviced();
4✔
370

371
            if(!m_status.should_exit()) {
4✔
372
               serve_one_session();
3✔
373
            }
374
         }
375
      }
4✔
376

377
      tcp::acceptor m_acceptor;
378
      tcp::resolver::iterator m_server_endpoints;
379

380
      std::shared_ptr<Botan::Credentials_Manager> m_creds;
381
      std::shared_ptr<Botan::TLS::Policy> m_policy;
382
      std::shared_ptr<Botan::TLS::Session_Manager> m_session_manager;
383
      ServerStatus m_status;
384
};
385

386
}  // namespace
387

388
class TLS_Proxy final : public Command {
389
   public:
390
      TLS_Proxy() :
2✔
391
            Command(
392
               "tls_proxy listen_port target_host target_port server_cert server_key "
393
               "--policy=default --threads=0 --max-clients=0 --session-db= --session-db-pass=") {}
4✔
394

395
      std::string group() const override { return "tls"; }
1✔
396

397
      std::string description() const override { return "Proxies requests between a TLS client and a TLS server"; }
1✔
398

399
      size_t thread_count() const {
1✔
400
         if(size_t t = get_arg_sz("threads")) {
1✔
401
            return t;
402
         }
403
         if(size_t t = Botan::OS::get_cpu_available()) {
1✔
404
            return t;
1✔
405
         }
406
         return 2;
407
      }
408

409
      void go() override {
1✔
410
         const uint16_t listen_port = get_arg_u16("listen_port");
1✔
411
         const std::string target = get_arg("target_host");
1✔
412
         const std::string target_port = get_arg("target_port");
1✔
413

414
         const std::string server_crt = get_arg("server_cert");
1✔
415
         const std::string server_key = get_arg("server_key");
1✔
416

417
         const size_t num_threads = thread_count();
1✔
418
         const size_t max_clients = get_arg_sz("max-clients");
1✔
419

420
         auto creds = std::make_shared<Basic_Credentials_Manager>(server_crt, server_key);
1✔
421

422
         auto policy = load_tls_policy(get_arg("policy"));
2✔
423

424
         boost::asio::io_service io;
1✔
425

426
         tcp::resolver resolver(io);
1✔
427
         auto server_endpoint_iterator = resolver.resolve({target, target_port});
1✔
428

429
         std::shared_ptr<Botan::TLS::Session_Manager> session_mgr;
1✔
430

431
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
432
         const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass");
2✔
433
         const std::string sessions_db = get_arg("session-db");
1✔
434

435
         if(!sessions_db.empty()) {
1✔
436
            session_mgr.reset(
×
437
               new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng_as_shared(), sessions_db));
×
438
         }
439
   #endif
440
         if(!session_mgr) {
1✔
441
            session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng_as_shared()));
2✔
442
         }
443

444
         tls_proxy_server server(io, listen_port, server_endpoint_iterator, creds, policy, session_mgr, max_clients);
2✔
445

446
         std::vector<std::shared_ptr<std::thread>> threads;
1✔
447

448
         // run forever... first thread is main calling io.run below
449
         for(size_t i = 2; i <= num_threads; ++i) {
2✔
450
            threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
2✔
451
         }
452

453
         io.run();
1✔
454

455
         for(size_t i = 0; i < threads.size(); ++i) {
2✔
456
            threads[i]->join();
1✔
457
         }
458
      }
7✔
459
};
460

461
BOTAN_REGISTER_COMMAND("tls_proxy", TLS_Proxy);
2✔
462

463
}  // namespace Botan_CLI
464

465
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc