Line data Source code
1 : // TCP Client & Server -- classes to ease handling sockets
2 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 : #pragma once
18 :
19 : // make sure we use OpenSSL with multi-thread support
20 : // (TODO: move to .cpp once we have the impl!)
21 : #define OPENSSL_THREAD_DEFINES
22 :
23 : // addr lib
24 : //
25 : #include "libaddr/addr.h"
26 :
27 : // Qt lib
28 : //
29 : #include <QString>
30 :
31 : // C++ lib
32 : //
33 : #include <stdexcept>
34 : #include <memory>
35 :
36 : // C lib
37 : //
38 : #include <arpa/inet.h>
39 :
40 : // OpenSSL lib
41 : //
42 : // BIO versions of the TCP client/server
43 : // TODO: move to an impl
44 : #include <openssl/bio.h>
45 : #include <openssl/err.h>
46 : #include <openssl/ssl.h>
47 :
48 : namespace tcp_client_server
49 : {
50 :
51 0 : class tcp_client_server_logic_error : public std::logic_error
52 : {
53 : public:
54 0 : tcp_client_server_logic_error(std::string const & errmsg) : logic_error(errmsg) {}
55 : };
56 :
57 0 : class tcp_client_server_runtime_error : public std::runtime_error
58 : {
59 : public:
60 0 : tcp_client_server_runtime_error(std::string const & errmsg) : runtime_error(errmsg) {}
61 : };
62 :
63 0 : class tcp_client_server_parameter_error : public tcp_client_server_logic_error
64 : {
65 : public:
66 0 : tcp_client_server_parameter_error(std::string const & errmsg) : tcp_client_server_logic_error(errmsg) {}
67 : };
68 :
69 0 : class tcp_client_server_initialization_error : public tcp_client_server_runtime_error
70 : {
71 : public:
72 0 : tcp_client_server_initialization_error(std::string const & errmsg) : tcp_client_server_runtime_error(errmsg) {}
73 : };
74 :
75 0 : class tcp_client_server_initialization_missing_error : public tcp_client_server_runtime_error
76 : {
77 : public:
78 0 : tcp_client_server_initialization_missing_error(std::string const & errmsg) : tcp_client_server_runtime_error(errmsg) {}
79 : };
80 :
81 :
82 :
83 : // TODO: assuming that bio_client with MODE_PLAIN works the same way
84 : // as a basic tcp_client, we should remove this class
85 : class tcp_client
86 : {
87 : public:
88 : typedef std::shared_ptr<tcp_client> pointer_t;
89 :
90 : tcp_client(std::string const & addr, int port);
91 : tcp_client(tcp_client const & src) = delete;
92 : tcp_client & operator = (tcp_client const & rhs) = delete;
93 : ~tcp_client();
94 :
95 : int get_socket() const;
96 : int get_port() const;
97 : int get_client_port() const;
98 : std::string get_addr() const;
99 : std::string get_client_addr() const;
100 :
101 : int read(char * buf, size_t size);
102 : int read_line(std::string & line);
103 : int write(char const * buf, size_t size);
104 :
105 : private:
106 : int f_socket = -1;
107 : int f_port = -1;
108 : std::string f_addr = std::string();
109 : };
110 :
111 :
112 : // TODO: implement a bio_server then like with the client remove
113 : // this basic tcp_server if it was like the bio version
114 : class tcp_server
115 : {
116 : public:
117 : typedef std::shared_ptr<tcp_server> pointer_t;
118 :
119 : static int const MAX_CONNECTIONS = 50;
120 :
121 : tcp_server(std::string const & addr, int port, int max_connections = MAX_CONNECTIONS, bool reuse_addr = false, bool auto_close = false);
122 : tcp_server(tcp_server const & src) = delete;
123 : tcp_server & operator = (tcp_server const & rhs) = delete;
124 : ~tcp_server();
125 :
126 : int get_socket() const;
127 : int get_max_connections() const;
128 : int get_port() const;
129 : std::string get_addr() const;
130 : bool get_keepalive() const;
131 : void set_keepalive(bool yes = true);
132 :
133 : int accept( int const max_wait_ms = -1 );
134 : int get_last_accepted_socket() const;
135 :
136 : private:
137 : int f_max_connections = MAX_CONNECTIONS;
138 : int f_socket = -1;
139 : int f_port = -1;
140 : std::string f_addr = std::string();
141 : int f_accepted_socket = -1;
142 : bool f_keepalive = true;
143 : bool f_auto_close = false;
144 : };
145 :
146 :
147 : class bio_server;
148 :
149 : // Create/manage certificates details:
150 : // https://help.ubuntu.com/lts/serverguide/certificates-and-security.html
151 : class bio_client
152 : {
153 : public:
154 : typedef std::shared_ptr<bio_client> pointer_t;
155 :
156 : enum class mode_t
157 : {
158 : MODE_PLAIN, // avoid SSL/TLS
159 : MODE_SECURE, // WARNING: may return a non-verified connection
160 : MODE_ALWAYS_SECURE // fails if cannot be 100% secure
161 : };
162 :
163 0 : class options
164 : {
165 : public:
166 : typedef uint32_t ssl_options_t;
167 : static int const MAX_VERIFICATION_DEPTH = 100;
168 : static ssl_options_t const DEFAULT_SSL_OPTIONS = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_COMPRESSION;
169 :
170 : options();
171 :
172 : void set_verification_depth(size_t depth);
173 : size_t get_verification_depth() const;
174 :
175 : void set_ssl_options(ssl_options_t ssl_options);
176 : ssl_options_t get_ssl_options() const;
177 :
178 : void set_ssl_certificate_path(std::string const path);
179 : std::string const & get_ssl_certificate_path() const;
180 : void set_keepalive(bool keepalive = true);
181 : bool get_keepalive() const;
182 :
183 : void set_sni(bool sni = true);
184 : bool get_sni() const;
185 :
186 : void set_host(std::string const & host);
187 : std::string const & get_host() const;
188 :
189 : private:
190 : size_t f_verification_depth = 4;
191 : uint32_t f_ssl_options = DEFAULT_SSL_OPTIONS;
192 : std::string f_ssl_certificate_path = std::string("/etc/ssl/certs");
193 : bool f_keepalive = true;
194 : bool f_sni = true;
195 : std::string f_host = std::string();
196 : };
197 :
198 : bio_client(std::string const & addr, int port, mode_t mode = mode_t::MODE_PLAIN, options const & opt = options());
199 : bio_client(bio_client const & src) = delete;
200 : bio_client & operator = (bio_client const & rhs) = delete;
201 : ~bio_client();
202 :
203 : void close();
204 :
205 : int get_socket() const;
206 : int get_port() const;
207 : int get_client_port() const;
208 : std::string get_addr() const;
209 : std::string get_client_addr() const;
210 :
211 : int read(char * buf, size_t size);
212 : int read_line(std::string & line);
213 : int write(char const * buf, size_t size);
214 :
215 : private:
216 : friend bio_server;
217 :
218 : bio_client(std::shared_ptr<BIO> bio);
219 :
220 : std::shared_ptr<SSL_CTX> f_ssl_ctx = std::shared_ptr<SSL_CTX>();
221 : std::shared_ptr<BIO> f_bio = std::shared_ptr<BIO>();
222 : };
223 :
224 :
225 : // try `man BIO_f_ssl` or go to:
226 : // https://www.openssl.org/docs/manmaster/crypto/BIO_f_ssl.html
227 0 : class bio_server
228 : {
229 : public:
230 : typedef std::shared_ptr<bio_server> pointer_t;
231 :
232 : static int const MAX_CONNECTIONS = 50;
233 :
234 : enum class mode_t
235 : {
236 : MODE_PLAIN, // no encryption
237 : MODE_SECURE // use TLS encryption
238 : };
239 :
240 : bio_server(addr::addr const & addr_port, int max_connections, bool reuse_addr, std::string const & certificate, std::string const & private_key, mode_t mode);
241 :
242 : bool is_secure() const;
243 : int get_socket() const;
244 : bio_client::pointer_t accept();
245 :
246 : private:
247 : int f_max_connections = MAX_CONNECTIONS;
248 : std::shared_ptr<SSL_CTX> f_ssl_ctx = std::shared_ptr<SSL_CTX>();
249 : std::shared_ptr<BIO> f_listen = std::shared_ptr<BIO>();
250 : bool f_keepalive = true;
251 : };
252 :
253 :
254 : void cleanup();
255 : void cleanup_on_thread_exit();
256 :
257 :
258 :
259 : bool is_ipv4(char const * ip);
260 : bool is_ipv6(char const * ip);
261 : void get_addr_port(QString const & addr_port, QString & addr, int & port, char const * protocol);
262 :
263 :
264 : } // namespace tcp_client_server
265 : // vim: ts=4 sw=4 et
|