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

randombit / botan / 5133556677

31 May 2023 02:11PM UTC coverage: 91.735% (-0.3%) from 92.012%
5133556677

Pull #3568

github

web-flow
Merge de48a2eb6 into 1cbeffafb
Pull Request #3568: Change clang-format AllowShortBlocksOnASingleLine from true to Empty

76059 of 82912 relevant lines covered (91.73%)

12004312.75 hits per line

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

67.97
/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) && defined(BOTAN_TARGET_OS_HAS_SOCKETS)
18

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

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

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

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

44
namespace Botan_CLI {
45

46
class TLS_Server;
47

48
namespace {
49

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

54
      std::ostream& output();
55
      void send(std::span<const uint8_t> buffer);
56
      void push_pending_output(std::string line);
57

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

62
         if(const auto& session_id = session.session_id(); !session_id.empty()) {
4✔
63
            output() << "Session ID " << Botan::hex_encode(session_id.get()) << std::endl;
8✔
64
         }
65

66
         if(const auto& session_ticket = session.session_ticket()) {
4✔
67
            output() << "Session ticket " << Botan::hex_encode(session_ticket->get()) << std::endl;
×
68
         }
69
      }
4✔
70

71
      void tls_record_received(uint64_t /*seq_no*/, std::span<const uint8_t> input) override {
4✔
72
         for(size_t i = 0; i != input.size(); ++i) {
916✔
73
            const char c = static_cast<char>(input[i]);
912✔
74
            m_line_buf += c;
912✔
75
            if(c == '\n') {
912✔
76
               push_pending_output(std::exchange(m_line_buf, {}));
12✔
77
            }
78
         }
79
      }
4✔
80

81
      void tls_emit_data(std::span<const uint8_t> buf) override { send(buf); }
54✔
82

83
      void tls_alert(Botan::TLS::Alert alert) override { output() << "Alert: " << alert.type_string() << std::endl; }
4✔
84

85
      std::string tls_server_choose_app_protocol(const std::vector<std::string>& /*client_protos*/) override {
×
86
         // we ignore whatever the client sends here
87
         return "echo/0.1";
×
88
      }
89

90
   private:
91
      TLS_Server& m_server_command;
92
      std::string m_line_buf;
93
};
94

95
}  // namespace
96

97
class TLS_Server final : public Command {
98
   public:
99
   #if defined(BOTAN_SO_SOCKETID)
100
      TLS_Server() :
2✔
101
            Command(
102
               "tls_server cert key --port=443 --psk= --psk-identity=  --type=tcp --policy=default --dump-traces= --max-clients=0 --socket-id=0")
4✔
103
   #else
104
      TLS_Server() :
105
            Command(
106
               "tls_server cert key --port=443 --psk= --psk-identity=  --type=tcp --policy=default --dump-traces= --max-clients=0")
107
   #endif
108
      {
109
         init_sockets();
2✔
110
      }
2✔
111

112
      ~TLS_Server() override { stop_sockets(); }
4✔
113

114
      TLS_Server(const TLS_Server& other) = delete;
115
      TLS_Server(TLS_Server&& other) = delete;
116
      TLS_Server& operator=(const TLS_Server& other) = delete;
117
      TLS_Server& operator=(TLS_Server&& other) = delete;
118

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

121
      std::string description() const override { return "Accept TLS/DTLS connections from TLS/DTLS clients"; }
1✔
122

123
      void go() override {
1✔
124
         const std::string server_crt = get_arg("cert");
1✔
125
         const std::string server_key = get_arg("key");
1✔
126
         const uint16_t port = get_arg_u16("port");
1✔
127
         const size_t max_clients = get_arg_sz("max-clients");
1✔
128
         const std::string transport = get_arg("type");
1✔
129
         const std::string dump_traces_to = get_arg("dump-traces");
1✔
130
         const auto psk = [this]() -> std::optional<Botan::SymmetricKey> {
2✔
131
            auto psk_hex = get_arg_maybe("psk");
1✔
132
            if(psk_hex) {
1✔
133
               return Botan::SymmetricKey(Botan::hex_decode_locked(psk_hex.value()));
1✔
134
            } else {
135
               return {};
1✔
136
            }
137
         }();
2✔
138
         const std::optional<std::string> psk_identity = get_arg_maybe("psk-identity");
1✔
139
   #if defined(BOTAN_SO_SOCKETID)
140
         m_socket_id = static_cast<uint32_t>(get_arg_sz("socket-id"));
1✔
141
   #endif
142

143
         if(transport != "tcp" && transport != "udp") {
1✔
144
            throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");
×
145
         }
146

147
         m_is_tcp = (transport == "tcp");
1✔
148

149
         auto policy = load_tls_policy(get_arg("policy"));
2✔
150
         auto session_manager =
1✔
151
            std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng_as_shared());  // TODO sqlite3
2✔
152
         auto creds = std::make_shared<Basic_Credentials_Manager>(server_crt, server_key, psk, psk_identity);
1✔
153
         auto callbacks = std::make_shared<Callbacks>(*this);
1✔
154

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

157
         if(!m_sandbox.init()) {
1✔
158
            error_output() << "Failed sandboxing\n";
×
159
            return;
×
160
         }
161

162
         socket_type server_fd = make_server_socket(port);
1✔
163
         size_t clients_served = 0;
164

165
         while(true) {
9✔
166
            if(max_clients > 0 && clients_served >= max_clients) {
5✔
167
               break;
168
            }
169

170
            if(m_is_tcp) {
4✔
171
               m_socket = ::accept(server_fd, nullptr, nullptr);
4✔
172
            } else {
173
               struct sockaddr_in from;
×
174
               socklen_t from_len = sizeof(sockaddr_in);
×
175

176
               void* peek_buf = nullptr;
×
177
               size_t peek_len = 0;
×
178

179
   #if defined(BOTAN_TARGET_OS_IS_MACOS)
180
               // macOS handles zero size buffers differently - it will return 0 even if there's no incoming data,
181
               // and after that connect() will fail as sockaddr_in from is not initialized
182
               int dummy;
183
               peek_buf = &dummy;
184
               peek_len = sizeof(dummy);
185
   #endif
186

187
               if(::recvfrom(server_fd,
×
188
                             static_cast<char*>(peek_buf),
189
                             static_cast<sendrecv_len_type>(peek_len),
190
                             MSG_PEEK,
191
                             reinterpret_cast<struct sockaddr*>(&from),
192
                             &from_len) != 0) {
193
                  throw CLI_Error("Could not peek next packet");
×
194
               }
195

196
               if(::connect(server_fd, reinterpret_cast<struct sockaddr*>(&from), from_len) != 0) {
×
197
                  throw CLI_Error("Could not connect UDP socket");
×
198
               }
199
               m_socket = server_fd;
×
200
            }
201

202
            clients_served++;
4✔
203

204
            Botan::TLS::Server server(callbacks, session_manager, creds, policy, rng_as_shared(), m_is_tcp == false);
24✔
205

206
            std::unique_ptr<std::ostream> dump_stream;
4✔
207

208
            if(!dump_traces_to.empty()) {
4✔
209
               uint64_t timestamp = Botan::OS::get_high_resolution_clock();
×
210
               const std::string dump_file = dump_traces_to + "/tls_" + std::to_string(timestamp) + ".bin";
×
211
               dump_stream = std::make_unique<std::ofstream>(dump_file.c_str());
×
212
            }
×
213

214
            try {
215
               while(!server.is_closed()) {
22✔
216
                  try {
18✔
217
                     uint8_t buf[4 * 1024] = {0};
18✔
218
                     ssize_t got = ::recv(m_socket, Botan::cast_uint8_ptr_to_char(buf), sizeof(buf), 0);
18✔
219

220
                     if(got == -1) {
18✔
221
                        error_output() << "Error in socket read - " << err_to_string(errno) << std::endl;
×
222
                        break;
×
223
                     }
224

225
                     if(got == 0) {
18✔
226
                        error_output() << "EOF on socket" << std::endl;
×
227
                        break;
228
                     }
229

230
                     if(dump_stream) {
18✔
231
                        dump_stream->write(reinterpret_cast<const char*>(buf), got);
×
232
                     }
233

234
                     server.received_data(buf, got);
18✔
235

236
                     while(server.is_active() && !m_pending_output.empty()) {
22✔
237
                        std::string output = m_pending_output.front();
4✔
238
                        m_pending_output.pop_front();
4✔
239
                        server.send(output);
4✔
240

241
                        if(output == "quit\n") {
4✔
242
                           server.close();
×
243
                        }
244
                     }
4✔
245
                  } catch(std::exception& e) {
×
246
                     error_output() << "Connection problem: " << e.what() << std::endl;
×
247
                     if(m_is_tcp) {
×
248
                        close_socket(m_socket);
×
249
                        m_socket = invalid_socket();
×
250
                     }
251
                  }
×
252
               }
253
            } catch(Botan::Exception& e) {
×
254
               error_output() << "Connection failed: " << e.what() << "\n";
×
255
            }
×
256

257
            if(m_is_tcp) {
4✔
258
               close_socket(m_socket);
4✔
259
               m_socket = invalid_socket();
4✔
260
            }
261
         }
4✔
262

263
         close_socket(server_fd);
1✔
264
      }
6✔
265

266
   public:
267
      using Command::flag_set;
268
      using Command::output;
269

270
      void send(std::span<const uint8_t> buf) {
27✔
271
         if(m_is_tcp) {
27✔
272
            ssize_t sent = ::send(m_socket, buf.data(), static_cast<sendrecv_len_type>(buf.size()), MSG_NOSIGNAL);
27✔
273

274
            if(sent == -1) {
27✔
275
               error_output() << "Error writing to socket - " << err_to_string(errno) << std::endl;
×
276
            } else if(sent != static_cast<ssize_t>(buf.size())) {
27✔
277
               error_output() << "Packet of length " << buf.size() << " truncated to " << sent << std::endl;
×
278
            }
279
         } else {
280
            while(!buf.empty()) {
×
281
               ssize_t sent = ::send(m_socket, buf.data(), static_cast<sendrecv_len_type>(buf.size()), MSG_NOSIGNAL);
×
282

283
               if(sent == -1) {
×
284
                  if(errno == EINTR) {
×
285
                     sent = 0;
286
                  } else {
287
                     throw CLI_Error("Socket write failed");
×
288
                  }
289
               }
290

291
               buf = buf.subspan(sent);
×
292
            }
293
         }
294
      }
27✔
295

296
      void push_pending_output(std::string line) { m_pending_output.emplace_back(std::move(line)); }
4✔
297

298
   private:
299
      socket_type make_server_socket(uint16_t port) {
1✔
300
         const int type = m_is_tcp ? SOCK_STREAM : SOCK_DGRAM;
1✔
301

302
         socket_type fd = ::socket(PF_INET, type, 0);
1✔
303
         if(fd == invalid_socket()) {
1✔
304
            throw CLI_Error("Unable to acquire socket");
×
305
         }
306

307
         sockaddr_in socket_info;
1✔
308
         Botan::clear_mem(&socket_info, 1);
1✔
309
         socket_info.sin_family = AF_INET;
1✔
310
         socket_info.sin_port = htons(port);
1✔
311

312
         // FIXME: support limiting listeners
313
         socket_info.sin_addr.s_addr = INADDR_ANY;
1✔
314

315
         if(::bind(fd, reinterpret_cast<struct sockaddr*>(&socket_info), sizeof(struct sockaddr)) != 0) {
1✔
316
            close_socket(fd);
×
317
            throw CLI_Error("server bind failed");
×
318
         }
319

320
         if(m_is_tcp) {
1✔
321
            constexpr int backlog = std::min(100, SOMAXCONN);
1✔
322
            if(::listen(fd, backlog) != 0) {
1✔
323
               close_socket(fd);
×
324
               throw CLI_Error("listen failed");
×
325
            }
326
         }
327
         if(m_socket_id > 0) {
1✔
328
   #if defined(BOTAN_SO_SOCKETID)
329
            if(::setsockopt(fd,
×
330
                            SOL_SOCKET,
331
                            BOTAN_SO_SOCKETID,
332
                            reinterpret_cast<const void*>(&m_socket_id),
×
333
                            sizeof(m_socket_id)) != 0) {
334
               // Failed but not world-ending issue
335
               output() << "set socket identifier setting failed" << std::endl;
×
336
            }
337
   #endif
338
         }
339
         return fd;
1✔
340
      }
341

342
      socket_type m_socket = invalid_socket();
343
      bool m_is_tcp = false;
344
      uint32_t m_socket_id = 0;
345
      std::list<std::string> m_pending_output;
346
      Sandbox m_sandbox;
347
};
348

349
namespace {
350

351
std::ostream& Callbacks::output() { return m_server_command.output(); }
12✔
352

353
void Callbacks::send(std::span<const uint8_t> buffer) { m_server_command.send(buffer); }
27✔
354

355
void Callbacks::push_pending_output(std::string line) { m_server_command.push_pending_output(std::move(line)); }
12✔
356

357
}  // namespace
358

359
BOTAN_REGISTER_COMMAND("tls_server", TLS_Server);
2✔
360

361
}  // namespace Botan_CLI
362

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