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

randombit / botan / 5080663405

25 May 2023 02:18PM UTC coverage: 91.675% (-0.01%) from 91.688%
5080663405

Pull #3549

github

Pull Request #3549: SPHINCS+

78519 of 85649 relevant lines covered (91.68%)

12135443.66 hits per line

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

70.41
/src/cli/tls_server.cpp
1
/*
2
* TLS echo server using BSD sockets
3
* (C) 2014 Jack Lloyd
4
*     2017 René Korthaus, Rohde & Schwarz Cybersecurity
5
*     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
#include "sandbox.h"
12

13
#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
14
  #include <sys/socket.h>
15
#endif
16

17
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && \
18
   defined(BOTAN_TARGET_OS_HAS_SOCKETS)
19

20
#if defined(SO_MARK) || defined(SO_USER_COOKIE) || defined(SO_RTABLE)
21
#if defined(SO_MARK)
22
#define BOTAN_SO_SOCKETID SO_MARK
23
#elif defined(SO_USER_COOKIE)
24
#define BOTAN_SO_SOCKETID SO_USER_COOKIE
25
#else
26
#define BOTAN_SO_SOCKETID SO_RTABLE
27
#endif
28
#endif
29

30
#include <botan/tls_callbacks.h>
31
#include <botan/tls_server.h>
32
#include <botan/tls_policy.h>
33
#include <botan/tls_session_manager_memory.h>
34
#include <botan/hex.h>
35
#include <botan/internal/os_utils.h>
36
#include <botan/mem_ops.h>
37

38
#include <list>
39
#include <fstream>
40
#include <memory>
41

42
#include "tls_helpers.h"
43
#include "socket_utils.h"
44

45
namespace Botan_CLI {
46

47
class TLS_Server;
48

49
namespace {
50

51
class Callbacks : public Botan::TLS::Callbacks
1✔
52
   {
53
   public:
54
      Callbacks(TLS_Server& server_command)
1✔
55
         : m_server_command(server_command) {}
1✔
56

57
      std::ostream& output();
58
      void send(std::span<const uint8_t> buffer);
59
      void push_pending_output(std::string line);
60

61
      void tls_session_established(const Botan::TLS::Session_Summary& session) override
4✔
62
         {
63
         output() << "Handshake complete, " << session.version().to_string()
4✔
64
                  << " using " << session.ciphersuite().to_string() << std::endl;
16✔
65

66
         if(const auto& session_id = session.session_id(); !session_id.empty())
4✔
67
            {
68
            output() << "Session ID " << Botan::hex_encode(session_id.get()) << std::endl;
8✔
69
            }
70

71
         if(const auto& session_ticket = session.session_ticket())
4✔
72
            {
73
            output() << "Session ticket " << Botan::hex_encode(session_ticket->get()) << std::endl;
×
74
            }
75
         }
4✔
76

77
      void tls_record_received(uint64_t /*seq_no*/, std::span<const uint8_t> input) override
4✔
78
         {
79
         for(size_t i = 0; i != input.size(); ++i)
916✔
80
            {
81
            const char c = static_cast<char>(input[i]);
912✔
82
            m_line_buf += c;
912✔
83
            if(c == '\n')
912✔
84
               {
85
               push_pending_output(std::exchange(m_line_buf, {}));
12✔
86
               }
87
            }
88
         }
4✔
89

90
      void tls_emit_data(std::span<const uint8_t> buf) override
27✔
91
         {
92
         send(buf);
54✔
93
         }
27✔
94

95
      void tls_alert(Botan::TLS::Alert alert) override
4✔
96
         {
97
         output() << "Alert: " << alert.type_string() << std::endl;
4✔
98
         }
4✔
99

100
      std::string tls_server_choose_app_protocol(const std::vector<std::string>& /*client_protos*/) override
×
101
         {
102
         // we ignore whatever the client sends here
103
         return "echo/0.1";
×
104
         }
105

106
   private:
107
      TLS_Server& m_server_command;
108
      std::string m_line_buf;
109
   };
110

111
}
112

113
class TLS_Server final : public Command
114
   {
115
   public:
116
#if defined(BOTAN_SO_SOCKETID)
117
      TLS_Server() : Command("tls_server cert key --port=443 --psk= --psk-identity=  --type=tcp --policy=default --dump-traces= --max-clients=0 --socket-id=0")
4✔
118
#else
119
      TLS_Server() : Command("tls_server cert key --port=443 --psk= --psk-identity=  --type=tcp --policy=default --dump-traces= --max-clients=0")
120
#endif
121
         {
122
         init_sockets();
2✔
123
         }
2✔
124

125
      ~TLS_Server() override
4✔
126
         {
2✔
127
         stop_sockets();
2✔
128
         }
4✔
129

130
      TLS_Server(const TLS_Server& other) = delete;
131
      TLS_Server(TLS_Server&& other) = delete;
132
      TLS_Server& operator=(const TLS_Server& other) = delete;
133
      TLS_Server& operator=(TLS_Server&& other) = delete;
134

135
      std::string group() const override
1✔
136
         {
137
         return "tls";
1✔
138
         }
139

140
      std::string description() const override
1✔
141
         {
142
         return "Accept TLS/DTLS connections from TLS/DTLS clients";
1✔
143
         }
144

145
      void go() override
1✔
146
         {
147
         const std::string server_crt = get_arg("cert");
1✔
148
         const std::string server_key = get_arg("key");
1✔
149
         const uint16_t port = get_arg_u16("port");
1✔
150
         const size_t max_clients = get_arg_sz("max-clients");
1✔
151
         const std::string transport = get_arg("type");
1✔
152
         const std::string dump_traces_to = get_arg("dump-traces");
1✔
153
         const auto psk = [this]() -> std::optional<Botan::SymmetricKey> {
2✔
154
            auto psk_hex = get_arg_maybe("psk");
1✔
155
            if(psk_hex)
1✔
156
               return Botan::SymmetricKey(Botan::hex_decode_locked(psk_hex.value()));
1✔
157
            else
158
               return {};
1✔
159
         }();
2✔
160
         const std::optional<std::string> psk_identity = get_arg_maybe("psk-identity");
1✔
161
#if defined(BOTAN_SO_SOCKETID)
162
         m_socket_id = static_cast<uint32_t>(get_arg_sz("socket-id"));
1✔
163
#endif
164

165
         if(transport != "tcp" && transport != "udp")
1✔
166
            {
167
            throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");
×
168
            }
169

170
         m_is_tcp = (transport == "tcp");
1✔
171

172
         auto policy = load_tls_policy(get_arg("policy"));
2✔
173
         auto session_manager = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng_as_shared()); // TODO sqlite3
2✔
174
         auto creds = std::make_shared<Basic_Credentials_Manager>(server_crt, server_key, psk, psk_identity);
1✔
175
         auto callbacks = std::make_shared<Callbacks>(*this);
1✔
176

177
         output() << "Listening for new connections on " << transport << " port " << port << std::endl;
1✔
178

179
         if(!m_sandbox.init())
1✔
180
            {
181
            error_output() << "Failed sandboxing\n";
×
182
            return;
×
183
            }
184

185
         socket_type server_fd = make_server_socket(port);
1✔
186
         size_t clients_served = 0;
187

188
         while(true)
9✔
189
            {
190
            if(max_clients > 0 && clients_served >= max_clients)
5✔
191
               break;
192

193
            if(m_is_tcp)
4✔
194
               {
195
               m_socket = ::accept(server_fd, nullptr, nullptr);
4✔
196
               }
197
            else
198
               {
199
               struct sockaddr_in from;
×
200
               socklen_t from_len = sizeof(sockaddr_in);
×
201

202
               void* peek_buf = nullptr;
×
203
               size_t peek_len = 0;
×
204

205
#if defined(BOTAN_TARGET_OS_IS_MACOS)
206
               // macOS handles zero size buffers differently - it will return 0 even if there's no incoming data,
207
               // and after that connect() will fail as sockaddr_in from is not initialized
208
               int dummy;
209
               peek_buf = &dummy;
210
               peek_len = sizeof(dummy);
211
#endif
212

213
               if(::recvfrom(server_fd, static_cast<char*>(peek_buf), static_cast<sendrecv_len_type>(peek_len),
×
214
                             MSG_PEEK, reinterpret_cast<struct sockaddr*>(&from), &from_len) != 0)
215
                  {
216
                  throw CLI_Error("Could not peek next packet");
×
217
                  }
218

219
               if(::connect(server_fd, reinterpret_cast<struct sockaddr*>(&from), from_len) != 0)
×
220
                  {
221
                  throw CLI_Error("Could not connect UDP socket");
×
222
                  }
223
               m_socket = server_fd;
×
224
               }
225

226
            clients_served++;
4✔
227

228
            Botan::TLS::Server server(
4✔
229
               callbacks,
230
               session_manager,
231
               creds,
232
               policy,
233
               rng_as_shared(),
×
234
               m_is_tcp == false);
24✔
235

236
            std::unique_ptr<std::ostream> dump_stream;
4✔
237

238
            if(!dump_traces_to.empty())
4✔
239
               {
240
               uint64_t timestamp = Botan::OS::get_high_resolution_clock();
×
241
               const std::string dump_file =
×
242
                  dump_traces_to + "/tls_" + std::to_string(timestamp) + ".bin";
×
243
               dump_stream = std::make_unique<std::ofstream>(dump_file.c_str());
×
244
               }
×
245

246
            try
247
               {
248
               while(!server.is_closed())
22✔
249
                  {
250
                  try
18✔
251
                     {
252
                     uint8_t buf[4 * 1024] = { 0 };
18✔
253
                     ssize_t got = ::recv(m_socket, Botan::cast_uint8_ptr_to_char(buf), sizeof(buf), 0);
18✔
254

255
                     if(got == -1)
18✔
256
                        {
257
                        error_output() << "Error in socket read - " << err_to_string(errno) << std::endl;
×
258
                        break;
×
259
                        }
260

261
                     if(got == 0)
18✔
262
                        {
263
                        error_output() << "EOF on socket" << std::endl;
×
264
                        break;
265
                        }
266

267
                     if(dump_stream)
18✔
268
                        {
269
                        dump_stream->write(reinterpret_cast<const char*>(buf), got);
×
270
                        }
271

272
                     server.received_data(buf, got);
18✔
273

274
                     while(server.is_active() && !m_pending_output.empty())
22✔
275
                        {
276
                        std::string output = m_pending_output.front();
4✔
277
                        m_pending_output.pop_front();
4✔
278
                        server.send(output);
4✔
279

280
                        if(output == "quit\n")
4✔
281
                           {
282
                           server.close();
×
283
                           }
284
                        }
4✔
285
                     }
286
                  catch(std::exception& e)
×
287
                     {
288
                     error_output() << "Connection problem: " << e.what() << std::endl;
×
289
                     if(m_is_tcp)
×
290
                        {
291
                        close_socket(m_socket);
×
292
                        m_socket = invalid_socket();
×
293
                        }
294
                     }
×
295
                  }
296
               }
297
            catch(Botan::Exception& e)
×
298
               {
299
               error_output() << "Connection failed: " << e.what() << "\n";
×
300
               }
×
301

302
            if(m_is_tcp)
4✔
303
               {
304
               close_socket(m_socket);
4✔
305
               m_socket = invalid_socket();
4✔
306
               }
307
            }
4✔
308

309
         close_socket(server_fd);
1✔
310
         }
6✔
311

312
   public:
313
      using Command::output;
314
      using Command::flag_set;
315

316
      void send(std::span<const uint8_t> buf)
27✔
317
         {
318
         if(m_is_tcp)
27✔
319
            {
320
            ssize_t sent = ::send(m_socket, buf.data(), static_cast<sendrecv_len_type>(buf.size()), MSG_NOSIGNAL);
27✔
321

322
            if(sent == -1)
27✔
323
               {
324
               error_output() << "Error writing to socket - " << err_to_string(errno) << std::endl;
×
325
               }
326
            else if(sent != static_cast<ssize_t>(buf.size()))
27✔
327
               {
328
               error_output() << "Packet of length " << buf.size() << " truncated to " << sent << std::endl;
×
329
               }
330
            }
331
         else
332
            {
333
            while(!buf.empty())
×
334
               {
335
               ssize_t sent = ::send(m_socket, buf.data(), static_cast<sendrecv_len_type>(buf.size()), MSG_NOSIGNAL);
×
336

337
               if(sent == -1)
×
338
                  {
339
                  if(errno == EINTR)
×
340
                     {
341
                     sent = 0;
342
                     }
343
                  else
344
                     {
345
                     throw CLI_Error("Socket write failed");
×
346
                     }
347
                  }
348

349
               buf = buf.subspan(sent);
×
350
               }
351
            }
352
         }
27✔
353

354
      void push_pending_output(std::string line)
4✔
355
         {
356
         m_pending_output.emplace_back(std::move(line));
4✔
357
         }
4✔
358

359
   private:
360
      socket_type make_server_socket(uint16_t port)
1✔
361
         {
362
         const int type = m_is_tcp ? SOCK_STREAM : SOCK_DGRAM;
1✔
363

364
         socket_type fd = ::socket(PF_INET, type, 0);
1✔
365
         if(fd == invalid_socket())
1✔
366
            {
367
            throw CLI_Error("Unable to acquire socket");
×
368
            }
369

370
         sockaddr_in socket_info;
1✔
371
         Botan::clear_mem(&socket_info, 1);
1✔
372
         socket_info.sin_family = AF_INET;
1✔
373
         socket_info.sin_port = htons(port);
1✔
374

375
         // FIXME: support limiting listeners
376
         socket_info.sin_addr.s_addr = INADDR_ANY;
1✔
377

378
         if(::bind(fd, reinterpret_cast<struct sockaddr*>(&socket_info), sizeof(struct sockaddr)) != 0)
1✔
379
            {
380
            close_socket(fd);
×
381
            throw CLI_Error("server bind failed");
×
382
            }
383

384
         if(m_is_tcp)
1✔
385
            {
386
            constexpr int backlog = std::min(100, SOMAXCONN);
1✔
387
            if(::listen(fd, backlog) != 0)
1✔
388
               {
389
               close_socket(fd);
×
390
               throw CLI_Error("listen failed");
×
391
               }
392
            }
393
         if(m_socket_id > 0)
1✔
394
            {
395
#if defined(BOTAN_SO_SOCKETID)
396
            if(::setsockopt(fd, SOL_SOCKET, BOTAN_SO_SOCKETID, reinterpret_cast<const void *>(&m_socket_id), sizeof(m_socket_id)) != 0)
×
397
               {
398
               // Failed but not world-ending issue
399
               output() << "set socket identifier setting failed" << std::endl;
×
400
               }
401
#endif
402
            }
403
         return fd;
1✔
404
         }
405

406
      socket_type m_socket = invalid_socket();
407
      bool m_is_tcp = false;
408
      uint32_t m_socket_id = 0;
409
      std::list<std::string> m_pending_output;
410
      Sandbox m_sandbox;
411
   };
412

413
namespace {
414

415
std::ostream& Callbacks::output()
12✔
416
   {
417
   return m_server_command.output();
12✔
418
   }
419

420
void Callbacks::send(std::span<const uint8_t> buffer)
27✔
421
   {
422
   m_server_command.send(buffer);
27✔
423
   }
424

425
void Callbacks::push_pending_output(std::string line)
4✔
426
   {
427
   m_server_command.push_pending_output(std::move(line));
12✔
428
   }
4✔
429

430
}
431

432
BOTAN_REGISTER_COMMAND("tls_server", TLS_Server);
2✔
433

434
}
435

436
#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