1+ /* ***********************************************************************************
2+ *
3+ * D++, A Lightweight C++ library for Discord
4+ *
5+ * SPDX-License-Identifier: Apache-2.0
6+ * Copyright 2021 Craig Edwards and D++ contributors
7+ * (https://github.com/brainboxdotcc/DPP/graphs/contributors)
8+ *
9+ * Licensed under the Apache License, Version 2.0 (the "License");
10+ * you may not use this file except in compliance with the License.
11+ * You may obtain a copy of the License at
12+ *
13+ * http://www.apache.org/licenses/LICENSE-2.0
14+ *
15+ * Unless required by applicable law or agreed to in writing, software
16+ * distributed under the License is distributed on an "AS IS" BASIS,
17+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+ * See the License for the specific language governing permissions and
19+ * limitations under the License.
20+ *
21+ ************************************************************************************/
22+ #include < dpp/ssl_context.h>
23+ #include < dpp/exception.h>
24+ #include < vector>
25+ #include < memory>
26+ #include < openssl/ssl.h>
27+ #include < mutex>
28+ #include < shared_mutex>
29+ #include < dpp/wrapped_ssl_ctx.h>
30+
31+ namespace dpp ::detail {
32+
33+ /* *
34+ * @brief The vector of pairs of wrapped contexts is efficient for small numbers of contexts.
35+ * In a real world production application we expect to have 2 to 5 at most contexts, and for
36+ * most bots that do not use server ports, there will be only one context on port 0.
37+ * This is O(n), but in the most common situation of having one entry, it is O(1).
38+ */
39+ static std::vector<std::pair<uint16_t , std::unique_ptr<wrapped_ssl_ctx>>> contexts;
40+
41+ /* *
42+ * @brief Managing SSL contexts is thread-safe.
43+ */
44+ static std::shared_mutex context_mutex;
45+
46+ void release_ssl_context (uint16_t port) {
47+ std::unique_lock lock (context_mutex);
48+ auto it = std::remove_if (contexts.begin (), contexts.end (), [port](const auto & entry) { return entry.first == port; });
49+ if (it != contexts.end ()) {
50+ contexts.erase (it, contexts.end ());
51+ }
52+ }
53+
54+ wrapped_ssl_ctx* generate_ssl_context (uint16_t port, const std::string &private_key, const std::string &public_key) {
55+ {
56+ std::shared_lock lock (context_mutex);
57+ for (const auto & [p, ctx] : contexts) {
58+ if (p == port) {
59+ return ctx.get ();
60+ }
61+ }
62+ }
63+
64+ std::unique_ptr<wrapped_ssl_ctx> context = std::make_unique<wrapped_ssl_ctx>(port != 0 );
65+
66+ if (port != 0 ) {
67+ if (SSL_CTX_use_certificate_file (context->context , public_key.c_str (), SSL_FILETYPE_PEM) <= 0 ) {
68+ throw dpp::connection_exception (err_ssl_context, " Failed to set public key certificate" );
69+ }
70+ if (SSL_CTX_use_PrivateKey_file (context->context , private_key.c_str (), SSL_FILETYPE_PEM) <= 0 ) {
71+ throw dpp::connection_exception (err_ssl_context, " Failed to set private key certificate" );
72+ }
73+ }
74+
75+ /* This sets the allowed SSL/TLS versions for the connection.
76+ * Do not allow SSL 3.0, TLS 1.0 or 1.1
77+ * https://www.packetlabs.net/posts/tls-1-1-no-longer-secure/
78+ */
79+ if (!SSL_CTX_set_min_proto_version (context->context , TLS1_2_VERSION)) {
80+ throw dpp::connection_exception (err_ssl_version, " Failed to set minimum SSL version!" );
81+ }
82+
83+ std::unique_lock lock (context_mutex);
84+ contexts.emplace_back (port, std::move (context));
85+ return contexts.back ().second .get ();
86+ }
87+
88+ }
0 commit comments