iodine 0.7.16 → 0.7.17

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -4
  3. data/.yardopts +8 -0
  4. data/CHANGELOG.md +26 -0
  5. data/LICENSE.txt +1 -1
  6. data/LIMITS.md +6 -0
  7. data/README.md +93 -13
  8. data/{SPEC-Websocket-Draft.md → SPEC-WebSocket-Draft.md} +0 -0
  9. data/examples/tcp_client.rb +66 -0
  10. data/examples/x-sendfile.ru +14 -0
  11. data/exe/iodine +3 -3
  12. data/ext/iodine/extconf.rb +21 -0
  13. data/ext/iodine/fio.c +659 -69
  14. data/ext/iodine/fio.h +350 -95
  15. data/ext/iodine/fio_cli.c +4 -3
  16. data/ext/iodine/fio_json_parser.h +1 -1
  17. data/ext/iodine/fio_siphash.c +13 -11
  18. data/ext/iodine/fio_siphash.h +6 -3
  19. data/ext/iodine/fio_tls.h +129 -0
  20. data/ext/iodine/fio_tls_missing.c +634 -0
  21. data/ext/iodine/fio_tls_openssl.c +1011 -0
  22. data/ext/iodine/fio_tmpfile.h +1 -1
  23. data/ext/iodine/fiobj.h +1 -1
  24. data/ext/iodine/fiobj_ary.c +1 -1
  25. data/ext/iodine/fiobj_ary.h +1 -1
  26. data/ext/iodine/fiobj_data.c +1 -1
  27. data/ext/iodine/fiobj_data.h +1 -1
  28. data/ext/iodine/fiobj_hash.c +1 -1
  29. data/ext/iodine/fiobj_hash.h +1 -1
  30. data/ext/iodine/fiobj_json.c +18 -16
  31. data/ext/iodine/fiobj_json.h +1 -1
  32. data/ext/iodine/fiobj_mustache.c +4 -0
  33. data/ext/iodine/fiobj_mustache.h +4 -0
  34. data/ext/iodine/fiobj_numbers.c +1 -1
  35. data/ext/iodine/fiobj_numbers.h +1 -1
  36. data/ext/iodine/fiobj_str.c +3 -3
  37. data/ext/iodine/fiobj_str.h +1 -1
  38. data/ext/iodine/fiobject.c +1 -1
  39. data/ext/iodine/fiobject.h +8 -2
  40. data/ext/iodine/http.c +128 -337
  41. data/ext/iodine/http.h +11 -18
  42. data/ext/iodine/http1.c +6 -6
  43. data/ext/iodine/http1.h +1 -1
  44. data/ext/iodine/http1_parser.c +1 -1
  45. data/ext/iodine/http1_parser.h +1 -1
  46. data/ext/iodine/http_internal.c +10 -8
  47. data/ext/iodine/http_internal.h +13 -3
  48. data/ext/iodine/http_mime_parser.h +1 -1
  49. data/ext/iodine/iodine.c +806 -22
  50. data/ext/iodine/iodine.h +33 -0
  51. data/ext/iodine/iodine_connection.c +23 -18
  52. data/ext/iodine/iodine_http.c +239 -225
  53. data/ext/iodine/iodine_http.h +4 -1
  54. data/ext/iodine/iodine_mustache.c +59 -54
  55. data/ext/iodine/iodine_pubsub.c +1 -1
  56. data/ext/iodine/iodine_tcp.c +34 -100
  57. data/ext/iodine/iodine_tcp.h +4 -0
  58. data/ext/iodine/iodine_tls.c +267 -0
  59. data/ext/iodine/iodine_tls.h +13 -0
  60. data/ext/iodine/mustache_parser.h +1 -1
  61. data/ext/iodine/redis_engine.c +14 -6
  62. data/ext/iodine/redis_engine.h +1 -1
  63. data/ext/iodine/resp_parser.h +1 -1
  64. data/ext/iodine/websocket_parser.h +1 -1
  65. data/ext/iodine/websockets.c +1 -1
  66. data/ext/iodine/websockets.h +1 -1
  67. data/iodine.gemspec +2 -1
  68. data/lib/iodine.rb +19 -5
  69. data/lib/iodine/connection.rb +13 -0
  70. data/lib/iodine/mustache.rb +7 -24
  71. data/lib/iodine/tls.rb +16 -0
  72. data/lib/iodine/version.rb +1 -1
  73. data/lib/rack/handler/iodine.rb +1 -1
  74. metadata +15 -5
@@ -0,0 +1,1011 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2018-2019
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #include <fio.h>
8
+
9
+ /**
10
+ * This implementation of the facil.io SSL/TLS wrapper API wraps the OpenSSL API
11
+ * to provide TLS 1.2 and TLS 1.3 to facil.io applications.
12
+ *
13
+ * The implementation requires `HAVE_OPENSSL` to be set.
14
+ */
15
+ #include "fio_tls.h"
16
+
17
+ #if HAVE_OPENSSL
18
+ #include <openssl/bio.h>
19
+ #include <openssl/err.h>
20
+ #include <openssl/ssl.h>
21
+
22
+ #define REQUIRE_LIBRARY()
23
+ #define FIO_TLS_WEAK
24
+
25
+ /* *****************************************************************************
26
+ The SSL/TLS helper data types (can be left as is)
27
+ ***************************************************************************** */
28
+ #define FIO_INCLUDE_STR 1
29
+ #define FIO_FORCE_MALLOC_TMP 1
30
+ #include <fio.h>
31
+
32
+ typedef struct {
33
+ fio_str_s private_key;
34
+ fio_str_s public_key;
35
+ fio_str_s password;
36
+ } cert_s;
37
+
38
+ static inline int fio_tls_cert_cmp(const cert_s *dest, const cert_s *src) {
39
+ return fio_str_iseq(&dest->private_key, &src->private_key);
40
+ }
41
+ static inline void fio_tls_cert_copy(cert_s *dest, cert_s *src) {
42
+ *dest = (cert_s){
43
+ .private_key = FIO_STR_INIT,
44
+ .public_key = FIO_STR_INIT,
45
+ .password = FIO_STR_INIT,
46
+ };
47
+ fio_str_concat(&dest->private_key, &src->private_key);
48
+ fio_str_concat(&dest->public_key, &src->public_key);
49
+ fio_str_concat(&dest->password, &src->password);
50
+ }
51
+ static inline void fio_tls_cert_destroy(cert_s *obj) {
52
+ fio_str_free(&obj->private_key);
53
+ fio_str_free(&obj->public_key);
54
+ fio_str_free(&obj->password);
55
+ }
56
+
57
+ #define FIO_ARY_NAME cert_ary
58
+ #define FIO_ARY_TYPE cert_s
59
+ #define FIO_ARY_COMPARE(k1, k2) (fio_tls_cert_cmp(&(k1), &(k2)))
60
+ #define FIO_ARY_COPY(dest, obj) fio_tls_cert_copy(&(dest), &(obj))
61
+ #define FIO_ARY_DESTROY(key) fio_tls_cert_destroy(&(key))
62
+ #define FIO_FORCE_MALLOC_TMP 1
63
+ #include <fio.h>
64
+
65
+ typedef struct {
66
+ fio_str_s pem;
67
+ } trust_s;
68
+
69
+ static inline int fio_tls_trust_cmp(const trust_s *dest, const trust_s *src) {
70
+ return fio_str_iseq(&dest->pem, &src->pem);
71
+ }
72
+ static inline void fio_tls_trust_copy(trust_s *dest, trust_s *src) {
73
+ *dest = (trust_s){
74
+ .pem = FIO_STR_INIT,
75
+ };
76
+ fio_str_concat(&dest->pem, &src->pem);
77
+ }
78
+ static inline void fio_tls_trust_destroy(trust_s *obj) {
79
+ fio_str_free(&obj->pem);
80
+ }
81
+
82
+ #define FIO_ARY_NAME trust_ary
83
+ #define FIO_ARY_TYPE trust_s
84
+ #define FIO_ARY_COMPARE(k1, k2) (fio_tls_trust_cmp(&(k1), &(k2)))
85
+ #define FIO_ARY_COPY(dest, obj) fio_tls_trust_copy(&(dest), &(obj))
86
+ #define FIO_ARY_DESTROY(key) fio_tls_trust_destroy(&(key))
87
+ #define FIO_FORCE_MALLOC_TMP 1
88
+ #include <fio.h>
89
+
90
+ typedef struct {
91
+ fio_str_s name; /* fio_str_s provides cache locality for small strings */
92
+ void (*on_selected)(intptr_t uuid, void *udata_connection, void *udata_tls);
93
+ void *udata_tls;
94
+ void (*on_cleanup)(void *udata_tls);
95
+ } alpn_s;
96
+
97
+ static inline int fio_alpn_cmp(const alpn_s *dest, const alpn_s *src) {
98
+ return fio_str_iseq(&dest->name, &src->name);
99
+ }
100
+ static inline void fio_alpn_copy(alpn_s *dest, alpn_s *src) {
101
+ *dest = (alpn_s){
102
+ .name = FIO_STR_INIT,
103
+ .on_selected = src->on_selected,
104
+ .udata_tls = src->udata_tls,
105
+ .on_cleanup = src->on_cleanup,
106
+ };
107
+ fio_str_concat(&dest->name, &src->name);
108
+ }
109
+ static inline void fio_alpn_destroy(alpn_s *obj) {
110
+ if (obj->on_cleanup)
111
+ obj->on_cleanup(obj->udata_tls);
112
+ fio_str_free(&obj->name);
113
+ }
114
+
115
+ #define FIO_SET_NAME alpn_list
116
+ #define FIO_SET_OBJ_TYPE alpn_s
117
+ #define FIO_SET_OBJ_COMPARE(k1, k2) fio_alpn_cmp(&(k1), &(k2))
118
+ #define FIO_SET_OBJ_COPY(dest, obj) fio_alpn_copy(&(dest), &(obj))
119
+ #define FIO_SET_OBJ_DESTROY(key) fio_alpn_destroy(&(key))
120
+ #define FIO_FORCE_MALLOC_TMP 1
121
+ #include <fio.h>
122
+
123
+ /* *****************************************************************************
124
+ The SSL/TLS type
125
+ ***************************************************************************** */
126
+
127
+ /** An opaque type used for the SSL/TLS functions. */
128
+ struct fio_tls_s {
129
+ size_t ref; /* Reference counter, to guards the ALPN registry */
130
+ alpn_list_s alpn; /* ALPN is the name for the protocol selection extension */
131
+
132
+ /*** the next two components could be optimized away with tweaking stuff ***/
133
+
134
+ cert_ary_s sni; /* SNI (server name extension) stores ID certificates */
135
+ trust_ary_s trust; /* Trusted certificate registry (peer verification) */
136
+
137
+ /************ TODO: implementation data fields go here ******************/
138
+
139
+ SSL_CTX *ctx; /* The Open SSL context (updated each time). */
140
+ unsigned char *alpn_str; /* the computed server-format ALPN string */
141
+ int alpn_len;
142
+ };
143
+
144
+ /* *****************************************************************************
145
+ ALPN Helpers
146
+ ***************************************************************************** */
147
+
148
+ /** Returns a pointer to the ALPN data (callback, etc') IF exists in the TLS. */
149
+ FIO_FUNC inline alpn_s *alpn_find(fio_tls_s *tls, char *name, size_t len) {
150
+ alpn_s tmp = {.name = FIO_STR_INIT_STATIC2(name, len)};
151
+ alpn_list__map_s_ *pos =
152
+ alpn_list__find_map_pos_(&tls->alpn, fio_str_hash(&tmp.name), tmp);
153
+ if (!pos || !pos->pos)
154
+ return NULL;
155
+ return &pos->pos->obj;
156
+ }
157
+
158
+ /** Adds an ALPN data object to the ALPN "list" (set) */
159
+ FIO_FUNC inline void alpn_add(
160
+ fio_tls_s *tls, const char *protocol_name,
161
+ void (*on_selected)(intptr_t uuid, void *udata_connection, void *udata_tls),
162
+ void *udata_tls, void (*on_cleanup)(void *udata_tls)) {
163
+ alpn_s tmp = {
164
+ .name = FIO_STR_INIT_STATIC(protocol_name),
165
+ .on_selected = on_selected,
166
+ .udata_tls = udata_tls,
167
+ .on_cleanup = on_cleanup,
168
+ };
169
+ if (fio_str_len(&tmp.name) > 255) {
170
+ FIO_LOG_ERROR("ALPN protocol names are limited to 255 bytes.");
171
+ return;
172
+ }
173
+ alpn_list_overwrite(&tls->alpn, fio_str_hash(&tmp.name), tmp, NULL);
174
+ tmp.on_cleanup = NULL;
175
+ fio_alpn_destroy(&tmp);
176
+ }
177
+
178
+ /** Returns a pointer to the default (first) ALPN object in the TLS (if any). */
179
+ FIO_FUNC inline alpn_s *alpn_default(fio_tls_s *tls) {
180
+ if (!tls || !alpn_list_count(&tls->alpn) || !tls->alpn.ordered)
181
+ return NULL;
182
+ return &tls->alpn.ordered[0].obj;
183
+ }
184
+
185
+ typedef struct {
186
+ alpn_s alpn;
187
+ intptr_t uuid;
188
+ void *udata_connection;
189
+ } alpn_task_s;
190
+
191
+ FIO_FUNC inline void alpn_select___task(void *t_, void *ignr_) {
192
+ alpn_task_s *t = t_;
193
+ t->alpn.on_selected((fio_is_valid(t->uuid) ? t->uuid : -1),
194
+ t->udata_connection, t->alpn.udata_tls);
195
+ fio_free(t);
196
+ (void)ignr_;
197
+ }
198
+
199
+ /** Schedules the ALPN protocol callback. */
200
+ FIO_FUNC inline void alpn_select(alpn_s *alpn, intptr_t uuid,
201
+ void *udata_connection) {
202
+ if (!alpn || !alpn->on_selected)
203
+ return;
204
+ alpn_task_s *t = fio_malloc(sizeof(*t));
205
+ *t = (alpn_task_s){
206
+ .alpn = *alpn,
207
+ .uuid = uuid,
208
+ .udata_connection = udata_connection,
209
+ };
210
+ fio_defer(alpn_select___task, t, NULL);
211
+ }
212
+
213
+ /* *****************************************************************************
214
+ OpenSSL Helpers
215
+ ***************************************************************************** */
216
+
217
+ static EVP_PKEY *fio_tls_pkey = NULL;
218
+
219
+ static void fio_tls_clear_root_key(void *key) {
220
+ EVP_PKEY_free(key);
221
+ fio_tls_pkey = NULL;
222
+ }
223
+
224
+ static void fio_tls_make_root_key(void) {
225
+ static fio_lock_i lock = FIO_LOCK_INIT;
226
+ fio_lock(&lock);
227
+ if (fio_tls_pkey)
228
+ goto finish;
229
+ /* create private key, free it at exit */
230
+ FIO_LOG_DEBUG("calculating a new TLS private key... might take a while.");
231
+
232
+ fio_tls_pkey = EVP_PKEY_new();
233
+ FIO_ASSERT(fio_tls_pkey, "OpenSSL failed to create private key.");
234
+
235
+ /* TODO: replace RSA with something else? is there something else? */
236
+ RSA *rsa = RSA_new();
237
+ BIGNUM *e = BN_new();
238
+ BN_clear(e);
239
+ BN_add_word(e, 65537);
240
+ FIO_ASSERT_ALLOC(e);
241
+ FIO_ASSERT(RSA_generate_key_ex(rsa, 2048, e, NULL),
242
+ "OpenSSL failed to create RSA key.");
243
+ BN_free(e);
244
+ EVP_PKEY_assign_RSA(fio_tls_pkey, rsa);
245
+ fio_state_callback_add(FIO_CALL_AT_EXIT, fio_tls_clear_root_key,
246
+ fio_tls_pkey);
247
+ finish:
248
+ fio_unlock(&lock);
249
+ }
250
+
251
+ static X509 *fio_tls_create_self_signed(char *server_name) {
252
+ X509 *cert = X509_new();
253
+ static uint32_t counter = 0;
254
+ FIO_ASSERT(cert,
255
+ "OpenSSL failed to allocate memory for self-signed ceritifcate.");
256
+ fio_tls_make_root_key();
257
+
258
+ /* serial number */
259
+ fio_atomic_add(&counter, 1);
260
+ ASN1_INTEGER_set(X509_get_serialNumber(cert), counter);
261
+
262
+ /* validity (180 days) */
263
+ X509_gmtime_adj(X509_get_notBefore(cert), 0);
264
+ X509_gmtime_adj(X509_get_notAfter(cert), 15552000L);
265
+
266
+ /* set (public) key */
267
+ X509_set_pubkey(cert, fio_tls_pkey);
268
+
269
+ /* set identity details */
270
+ X509_NAME *s = X509_get_subject_name(cert);
271
+ size_t srv_name_len = strlen(server_name);
272
+ X509_NAME_add_entry_by_txt(s, "O", MBSTRING_ASC, (unsigned char *)server_name,
273
+ srv_name_len, -1, 0);
274
+ X509_NAME_add_entry_by_txt(s, "CN", MBSTRING_ASC,
275
+ (unsigned char *)server_name, srv_name_len, -1, 0);
276
+ X509_NAME_add_entry_by_txt(s, "CA", MBSTRING_ASC,
277
+ (unsigned char *)server_name, srv_name_len, -1, 0);
278
+ X509_set_issuer_name(cert, s);
279
+
280
+ /* sign certificate */
281
+ FIO_ASSERT(X509_sign(cert, fio_tls_pkey, EVP_sha512()),
282
+ "OpenSSL failed to signe self-signed certificate");
283
+ // FILE *fp = fopen("tmp.pem", "ab+");
284
+ // if (fp) {
285
+ // PEM_write_X509(fp, cert);
286
+ // fclose(fp);
287
+ // }
288
+
289
+ return cert;
290
+ }
291
+
292
+ /* *****************************************************************************
293
+ SSL/TLS Context (re)-building
294
+ ***************************************************************************** */
295
+
296
+ #define TLS_BUFFER_LENGTH (1 << 15)
297
+ typedef struct {
298
+ SSL *ssl;
299
+ fio_tls_s *tls;
300
+ void *alpn_arg;
301
+ intptr_t uuid;
302
+ uint8_t is_server;
303
+ volatile uint8_t alpn_ok;
304
+ } fio_tls_connection_s;
305
+
306
+ static void fio_tls_alpn_fallback(fio_tls_connection_s *c) {
307
+ alpn_s *alpn = alpn_default(c->tls);
308
+ if (!alpn || !alpn->on_selected)
309
+ return;
310
+ /* set protocol to default protocol */
311
+ FIO_LOG_DEBUG("TLS ALPN handshake missing, falling back on %s for %p",
312
+ fio_str_info(&alpn->name).data, (void *)c->uuid);
313
+ alpn_select(alpn, c->uuid, c->alpn_arg);
314
+ }
315
+ static int fio_tls_alpn_selector_cb(SSL *ssl, const unsigned char **out,
316
+ unsigned char *outlen,
317
+ const unsigned char *in, unsigned int inlen,
318
+ void *tls_) {
319
+ fio_tls_s *tls = tls_;
320
+ alpn_s *alpn;
321
+ /* TODO: select ALPN and call on_selected */
322
+ fio_tls_connection_s *c = SSL_get_ex_data(ssl, 0);
323
+ c->alpn_ok = 1;
324
+
325
+ if (alpn_list_count(&tls->alpn) == 0)
326
+ return SSL_TLSEXT_ERR_NOACK;
327
+ const unsigned char *end = in + inlen;
328
+ while (in < end) {
329
+ uint8_t l = in[0];
330
+ alpn = alpn_find(tls, (char *)in + 1, l);
331
+ in += l + 1;
332
+ if (!alpn)
333
+ continue;
334
+ fio_str_info_s info = fio_str_info(&alpn->name);
335
+ *out = (unsigned char *)info.data;
336
+ *outlen = (unsigned char)info.len;
337
+ FIO_LOG_DEBUG("TLS ALPN set to: %s for %p", info.data, (void *)c->uuid);
338
+ alpn_select(alpn, c->uuid, c->alpn_arg);
339
+ return SSL_TLSEXT_ERR_OK;
340
+ }
341
+ /* set protocol to default protocol */
342
+ alpn = alpn_default(tls);
343
+ alpn_select(alpn, c->uuid, c->alpn_arg);
344
+ FIO_LOG_DEBUG(
345
+ "TLS ALPN handshake failed, falling back on default (%s) for %p",
346
+ fio_str_data(&alpn->name), (void *)c->uuid);
347
+ return SSL_TLSEXT_ERR_NOACK;
348
+ (void)ssl;
349
+ (void)out;
350
+ (void)outlen;
351
+ (void)in;
352
+ (void)inlen;
353
+ (void)tls;
354
+ }
355
+
356
+ /** Called when the library specific data for the context should be destroyed */
357
+ static void fio_tls_destroy_context(fio_tls_s *tls) {
358
+ /* TODO: Library specific implementation */
359
+ SSL_CTX_free(tls->ctx);
360
+ free(tls->alpn_str);
361
+
362
+ tls->ctx = NULL;
363
+ tls->alpn_str = NULL;
364
+ tls->alpn_len = 0;
365
+ FIO_LOG_DEBUG("destroyed TLS context for OpenSSL %p", (void *)tls);
366
+ }
367
+
368
+ static int fio_tls_pem_passwd_cb(char *buf, int size, int rwflag,
369
+ void *password) {
370
+ fio_str_info_s *p = password;
371
+ if (!p || !p->len || !size)
372
+ return 0;
373
+ int len = (size <= (int)p->len) ? (size - 1) : (int)p->len;
374
+ memcpy(buf, p->data, len);
375
+ buf[len] = 0;
376
+ return len;
377
+ (void)rwflag;
378
+ }
379
+
380
+ /** Called when the library specific data for the context should be built */
381
+ static void fio_tls_build_context(fio_tls_s *tls) {
382
+ fio_tls_destroy_context(tls);
383
+ /* TODO: Library specific implementation */
384
+
385
+ /* create new context */
386
+ tls->ctx = SSL_CTX_new(TLS_method());
387
+ SSL_CTX_set_mode(tls->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
388
+ /* see: https://caniuse.com/#search=tls */
389
+ SSL_CTX_set_min_proto_version(tls->ctx, TLS1_2_VERSION);
390
+ SSL_CTX_set_options(tls->ctx, SSL_OP_NO_COMPRESSION);
391
+
392
+ /* attach certificates */
393
+ FIO_ARY_FOR(&tls->sni, pos) {
394
+ fio_str_info_s keys[4] = {
395
+ fio_str_info(&pos->private_key), fio_str_info(&pos->public_key),
396
+ fio_str_info(&pos->password),
397
+ /* empty password slot for public key */
398
+ };
399
+ if (keys[0].len && keys[1].len) {
400
+ if (1) {
401
+ /* Extract private key from private key file */
402
+ BIO *bio = BIO_new_mem_buf(keys[0].data, keys[0].len);
403
+ if (bio) {
404
+ EVP_PKEY *k = PEM_read_bio_PrivateKey(
405
+ bio, NULL, fio_tls_pem_passwd_cb, keys + 2);
406
+ if (k) {
407
+ FIO_LOG_DEBUG("TLS read private key from PEM file.");
408
+ SSL_CTX_use_PrivateKey(tls->ctx, k);
409
+ }
410
+ BIO_free(bio);
411
+ }
412
+ }
413
+ /* Certificate Files loaded */
414
+ for (int ki = 0; ki < 2; ++ki) {
415
+ /* Extract as much data as possible from each file */
416
+ BIO *bio = BIO_new_mem_buf(keys[ki].data, keys[ki].len);
417
+ FIO_ASSERT(bio, "OpenSSL error allocating BIO.");
418
+ STACK_OF(X509_INFO) *inf = PEM_X509_INFO_read_bio(
419
+ bio, NULL, fio_tls_pem_passwd_cb, keys + ki + 2);
420
+ if (inf) {
421
+ for (int i = 0; i < sk_X509_INFO_num(inf); ++i) {
422
+ /* for each element in PEM */
423
+ X509_INFO *tmp = sk_X509_INFO_value(inf, i);
424
+ if (tmp->x509) {
425
+ FIO_LOG_DEBUG("TLS adding certificate from PEM file.");
426
+ SSL_CTX_use_certificate(tls->ctx, tmp->x509);
427
+ }
428
+ if (tmp->x_pkey) {
429
+ FIO_LOG_DEBUG("TLS adding private key from PEM file.");
430
+ SSL_CTX_use_PrivateKey(tls->ctx, tmp->x_pkey->dec_pkey);
431
+ }
432
+ }
433
+ sk_X509_INFO_pop_free(inf, X509_INFO_free);
434
+ } else {
435
+ /* TODO: attempt DER format? */
436
+ // X509 *c;
437
+ // EVP_PKEY *k;
438
+ // const uint8_t *pdata = (uint8_t *)&keys[ki].data;
439
+ // d2i_X509(&c, &pdata, keys[ki].len);
440
+ // pdata = (uint8_t *)&keys[ki].data;
441
+ // d2i_AutoPrivateKey(&k, &pdata, keys[ki].len);
442
+ }
443
+ BIO_free(bio);
444
+ }
445
+ } else if (keys[0].len) {
446
+ /* Self Signed Certificates, only if server name is provided. */
447
+ SSL_CTX_use_certificate(tls->ctx,
448
+ fio_tls_create_self_signed(keys[0].data));
449
+ SSL_CTX_use_PrivateKey(tls->ctx, fio_tls_pkey);
450
+ }
451
+ }
452
+
453
+ /* setup ALPN support */
454
+ if (1) {
455
+ size_t alpn_pos = 0;
456
+ /* looping twice is better than malloc fragmentation. */
457
+ FIO_SET_FOR_LOOP(&tls->alpn, pos) {
458
+ fio_str_info_s s = fio_str_info(&pos->obj.name);
459
+ if (!s.len)
460
+ continue;
461
+ alpn_pos += s.len + 1;
462
+ }
463
+ tls->alpn_str = malloc((alpn_pos | 15) + 1); /* round up to 16 + padding */
464
+ alpn_pos = 0;
465
+ FIO_SET_FOR_LOOP(&tls->alpn, pos) {
466
+ fio_str_info_s s = fio_str_info(&pos->obj.name);
467
+ if (!s.len)
468
+ continue;
469
+ tls->alpn_str[alpn_pos++] = (uint8_t)s.len;
470
+ memcpy(tls->alpn_str + alpn_pos, s.data, s.len);
471
+ alpn_pos += s.len;
472
+ }
473
+ tls->alpn_len = alpn_pos;
474
+ SSL_CTX_set_alpn_select_cb(tls->ctx, fio_tls_alpn_selector_cb, tls);
475
+ SSL_CTX_set_alpn_protos(tls->ctx, tls->alpn_str, tls->alpn_len);
476
+ }
477
+
478
+ /* Peer Verification / Trust */
479
+ if (trust_ary_count(&tls->trust)) {
480
+ /* TODO: enable peer verification */
481
+ X509_STORE *store = X509_STORE_new();
482
+ SSL_CTX_set_cert_store(tls->ctx, store);
483
+ SSL_CTX_set_verify(tls->ctx, SSL_VERIFY_PEER, NULL);
484
+ /* TODO: Add each ceriticate in the PEM to the trust "store" */
485
+ FIO_ARY_FOR(&tls->trust, pos) {
486
+ fio_str_info_s pem = fio_str_info(&pos->pem);
487
+ BIO *bio = BIO_new_mem_buf(pem.data, pem.len);
488
+ FIO_ASSERT(bio, "OpenSSL error allocating BIO.");
489
+ STACK_OF(X509_INFO) *inf = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
490
+ if (inf) {
491
+ for (int i = 0; i < sk_X509_INFO_num(inf); ++i) {
492
+ /* for each element in PEM */
493
+ X509_INFO *tmp = sk_X509_INFO_value(inf, i);
494
+ if (tmp->x509) {
495
+ FIO_LOG_DEBUG("TLS trusting certificate from PEM file.");
496
+ X509_STORE_add_cert(store, tmp->x509);
497
+ }
498
+ if (tmp->crl) {
499
+ X509_STORE_add_crl(store, tmp->crl);
500
+ }
501
+ }
502
+ sk_X509_INFO_pop_free(inf, X509_INFO_free);
503
+ }
504
+ BIO_free(bio);
505
+ }
506
+ }
507
+
508
+ FIO_LOG_DEBUG("(re)built TLS context for OpenSSL %p", (void *)tls);
509
+ }
510
+
511
+ /* *****************************************************************************
512
+ SSL/TLS RW Hooks
513
+ ***************************************************************************** */
514
+
515
+ static void fio_tls_delayed_close(void *uuid, void *ignr_) {
516
+ fio_close((intptr_t)uuid);
517
+ (void)ignr_;
518
+ }
519
+
520
+ /* TODO: this is an example implementation - fix for specific library. */
521
+
522
+ /**
523
+ * Implement reading from a file descriptor. Should behave like the file
524
+ * system `read` call, including the setup or errno to EAGAIN / EWOULDBLOCK.
525
+ *
526
+ * Note: facil.io library functions MUST NEVER be called by any r/w hook, or a
527
+ * deadlock might occur.
528
+ */
529
+ static ssize_t fio_tls_read(intptr_t uuid, void *udata, void *buf,
530
+ size_t count) {
531
+ fio_tls_connection_s *c = udata;
532
+ ssize_t ret = SSL_read(c->ssl, buf, count);
533
+ if (ret > 0)
534
+ return ret;
535
+ ret = SSL_get_error(c->ssl, ret);
536
+ switch (ret) {
537
+ case SSL_ERROR_SSL: /* overflow */
538
+ case SSL_ERROR_ZERO_RETURN:
539
+ return 0; /* EOF */
540
+ case SSL_ERROR_NONE: /* overflow */
541
+ case SSL_ERROR_WANT_CONNECT: /* overflow */
542
+ case SSL_ERROR_WANT_ACCEPT: /* overflow */
543
+ case SSL_ERROR_WANT_X509_LOOKUP: /* overflow */
544
+ #ifdef SSL_ERROR_WANT_ASYNC
545
+ case SSL_ERROR_WANT_ASYNC: /* overflow */
546
+ #endif
547
+ case SSL_ERROR_WANT_WRITE: /* overflow */
548
+ case SSL_ERROR_WANT_READ:
549
+ default:
550
+ break;
551
+ }
552
+ errno = EWOULDBLOCK;
553
+ return -1;
554
+ (void)uuid;
555
+ }
556
+
557
+ /**
558
+ * When implemented, this function will be called to flush any data remaining
559
+ * in the internal buffer.
560
+ *
561
+ * The function should return the number of bytes remaining in the internal
562
+ * buffer (0 is a valid response) or -1 (on error).
563
+ *
564
+ * Note: facil.io library functions MUST NEVER be called by any r/w hook, or a
565
+ * deadlock might occur.
566
+ */
567
+ static ssize_t fio_tls_flush(intptr_t uuid, void *udata) {
568
+ (void)uuid;
569
+ (void)udata;
570
+ return 0;
571
+ }
572
+
573
+ /**
574
+ * Implement writing to a file descriptor. Should behave like the file system
575
+ * `write` call.
576
+ *
577
+ * If an internal buffer is implemented and it is full, errno should be set to
578
+ * EWOULDBLOCK and the function should return -1.
579
+ *
580
+ * Note: facil.io library functions MUST NEVER be called by any r/w hook, or a
581
+ * deadlock might occur.
582
+ */
583
+ static ssize_t fio_tls_write(intptr_t uuid, void *udata, const void *buf,
584
+ size_t count) {
585
+ fio_tls_connection_s *c = udata;
586
+ ssize_t ret = SSL_write(c->ssl, buf, count);
587
+ if (ret > 0)
588
+ return ret;
589
+ ret = SSL_get_error(c->ssl, ret);
590
+ switch (ret) {
591
+ case SSL_ERROR_SSL: /* overflow */
592
+ case SSL_ERROR_ZERO_RETURN:
593
+ return 0; /* EOF */
594
+ case SSL_ERROR_NONE: /* overflow */
595
+ case SSL_ERROR_WANT_CONNECT: /* overflow */
596
+ case SSL_ERROR_WANT_ACCEPT: /* overflow */
597
+ case SSL_ERROR_WANT_X509_LOOKUP: /* overflow */
598
+ #ifdef SSL_ERROR_WANT_ASYNC
599
+ case SSL_ERROR_WANT_ASYNC: /* overflow */
600
+ #endif
601
+ case SSL_ERROR_WANT_WRITE: /* overflow */
602
+ case SSL_ERROR_WANT_READ:
603
+ default:
604
+ break;
605
+ }
606
+ errno = EWOULDBLOCK;
607
+ return -1;
608
+ (void)uuid;
609
+ }
610
+
611
+ /**
612
+ * The `close` callback should close the underlying socket / file descriptor.
613
+ *
614
+ * If the function returns a non-zero value, it will be called again after an
615
+ * attempt to flush the socket and any pending outgoing buffer.
616
+ *
617
+ * Note: facil.io library functions MUST NEVER be called by any r/w hook, or a
618
+ * deadlock might occur.
619
+ * */
620
+ static ssize_t fio_tls_before_close(intptr_t uuid, void *udata) {
621
+ fio_tls_connection_s *c = udata;
622
+ SSL_shutdown(c->ssl);
623
+ return 1;
624
+ (void)uuid;
625
+ }
626
+ /**
627
+ * Called to perform cleanup after the socket was closed.
628
+ * */
629
+ static void fio_tls_cleanup(void *udata) {
630
+ fio_tls_connection_s *c = udata;
631
+ if (!c->alpn_ok) {
632
+ alpn_select(alpn_default(c->tls), -1, c->alpn_arg);
633
+ }
634
+ SSL_free(c->ssl);
635
+ FIO_LOG_DEBUG("TLS cleanup for %p", (void *)c->uuid);
636
+ fio_tls_destroy(c->tls); /* manage reference count */
637
+ free(udata);
638
+ }
639
+
640
+ static fio_rw_hook_s FIO_TLS_HOOKS = {
641
+ .read = fio_tls_read,
642
+ .write = fio_tls_write,
643
+ .before_close = fio_tls_before_close,
644
+ .flush = fio_tls_flush,
645
+ .cleanup = fio_tls_cleanup,
646
+ };
647
+
648
+ static size_t fio_tls_handshake(intptr_t uuid, void *udata) {
649
+ fio_tls_connection_s *c = udata;
650
+ int ri;
651
+ if (c->is_server) {
652
+ ri = SSL_accept(c->ssl);
653
+ } else {
654
+ ri = SSL_connect(c->ssl);
655
+ }
656
+ if (ri != 1) {
657
+ ri = SSL_get_error(c->ssl, ri);
658
+ switch (ri) {
659
+ case SSL_ERROR_NONE:
660
+ // FIO_LOG_DEBUG("SSL_accept/SSL_connect %p state: SSL_ERROR_NONE",
661
+ // (void *)uuid);
662
+ return 0;
663
+ case SSL_ERROR_WANT_WRITE:
664
+ // FIO_LOG_DEBUG("SSL_accept/SSL_connect %p state: SSL_ERROR_WANT_WRITE",
665
+ // (void *)uuid);
666
+ // fio_force_event(uuid, FIO_EVENT_ON_READY);
667
+ return 0;
668
+ case SSL_ERROR_WANT_READ:
669
+ // FIO_LOG_DEBUG("SSL_accept/SSL_connect %p state: SSL_ERROR_WANT_READ",
670
+ // (void *)uuid);
671
+ // fio_force_event(uuid, FIO_EVENT_ON_DATA);
672
+ return 0;
673
+ case SSL_ERROR_SYSCALL:
674
+ FIO_LOG_DEBUG(
675
+ "SSL_accept/SSL_connect %p error: SSL_ERROR_SYSCALL, errno: %s",
676
+ (void *)uuid, strerror(errno));
677
+ // fio_force_event(uuid, FIO_EVENT_ON_DATA);
678
+ return 0;
679
+ case SSL_ERROR_SSL:
680
+ FIO_LOG_DEBUG("SSL_accept/SSL_connect %p error: SSL_ERROR_SSL",
681
+ (void *)uuid);
682
+ break;
683
+ case SSL_ERROR_ZERO_RETURN:
684
+ FIO_LOG_DEBUG("SSL_accept/SSL_connect %p error: SSL_ERROR_ZERO_RETURN",
685
+ (void *)uuid);
686
+ break;
687
+ case SSL_ERROR_WANT_CONNECT:
688
+ FIO_LOG_DEBUG("SSL_accept/SSL_connect %p error: SSL_ERROR_WANT_CONNECT",
689
+ (void *)uuid);
690
+ break;
691
+ case SSL_ERROR_WANT_ACCEPT:
692
+ FIO_LOG_DEBUG("SSL_accept/SSL_connect %p error: SSL_ERROR_WANT_ACCEPT",
693
+ (void *)uuid);
694
+ break;
695
+ case SSL_ERROR_WANT_X509_LOOKUP:
696
+ FIO_LOG_DEBUG(
697
+ "SSL_accept/SSL_connect %p error: SSL_ERROR_WANT_X509_LOOKUP",
698
+ (void *)uuid);
699
+ break;
700
+ #ifdef SSL_ERROR_WANT_ASYNC
701
+ case SSL_ERROR_WANT_ASYNC:
702
+ FIO_LOG_DEBUG("SSL_accept/SSL_connect %p error: SSL_ERROR_WANT_ASYNC",
703
+ (void *)uuid);
704
+ break;
705
+ #endif
706
+ #ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
707
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB:
708
+ FIO_LOG_DEBUG(
709
+ "SSL_accept/SSL_connect %p error: SSL_ERROR_WANT_CLIENT_HELLO_CB",
710
+ (void *)uuid);
711
+ break;
712
+ #endif
713
+ default:
714
+ FIO_LOG_DEBUG("SSL_accept/SSL_connect %p error: unknown (%d).",
715
+ (void *)uuid, ri);
716
+ break;
717
+ }
718
+ fio_defer(fio_tls_delayed_close, (void *)uuid, NULL);
719
+ return 0;
720
+ }
721
+ if (!c->alpn_ok) {
722
+ c->alpn_ok = 1;
723
+ if (c->is_server) {
724
+ fio_tls_alpn_fallback(c);
725
+ } else {
726
+ const unsigned char *proto;
727
+ unsigned int proto_len;
728
+ SSL_get0_alpn_selected(c->ssl, &proto, &proto_len);
729
+ alpn_s *alpn = NULL;
730
+ if (proto_len > 0) {
731
+ alpn = alpn_find(c->tls, (char *)proto, proto_len);
732
+ }
733
+ if (!alpn) {
734
+ alpn = alpn_default(c->tls);
735
+ FIO_LOG_DEBUG("ALPN missing for TLS client %p", (void *)uuid);
736
+ }
737
+ if (alpn)
738
+ FIO_LOG_DEBUG("setting ALPN %s for TLS client %p",
739
+ fio_str_data(&alpn->name), (void *)uuid);
740
+ alpn_select(alpn, c->uuid, c->alpn_arg);
741
+ }
742
+ }
743
+ if (fio_rw_hook_replace_unsafe(uuid, &FIO_TLS_HOOKS, udata) == 0) {
744
+ FIO_LOG_DEBUG("Completed TLS handshake for %p", (void *)uuid);
745
+ } else {
746
+ FIO_LOG_DEBUG("Something went wrong during TLS handshake for %p",
747
+ (void *)uuid);
748
+ return 0;
749
+ }
750
+ /* make sure the connection is re-added to the reactor */
751
+ fio_force_event(uuid, FIO_EVENT_ON_DATA);
752
+ /* log session ID for WireShark */
753
+ #if FIO_TLS_PRINT_SECRET
754
+ if (FIO_LOG_LEVEL >= FIO_LOG_LEVEL_DEBUG) {
755
+ unsigned char buff[SSL_MAX_MASTER_KEY_LENGTH + 2];
756
+ size_t ret = SSL_SESSION_get_master_key(SSL_get_session(c->ssl), buff,
757
+ SSL_MAX_MASTER_KEY_LENGTH + 1);
758
+ buff[ret] = 0;
759
+ unsigned char buff2[(SSL_MAX_MASTER_KEY_LENGTH + 2) << 1];
760
+ for (size_t i = 0; i < ret; ++i) {
761
+ buff2[i] = ((buff[i] >> 4) >= 10) ? ('A' + (buff[i] >> 4) - 10)
762
+ : ('0' + (buff[i] >> 4));
763
+ buff2[i + 1] = ((buff[i] & 15) >= 10) ? ('A' + (buff[i] & 15) - 10)
764
+ : ('0' + (buff[i] & 15));
765
+ }
766
+ buff2[(ret << 1)] = 0;
767
+ FIO_LOG_DEBUG("OpenSSL Master Key for uuid %p:\n\t\t%s", (void *)uuid,
768
+ buff2);
769
+ }
770
+ #endif
771
+ return 1;
772
+ }
773
+
774
+ static ssize_t fio_tls_read4handshake(intptr_t uuid, void *udata, void *buf,
775
+ size_t count) {
776
+ // FIO_LOG_DEBUG("TLS handshake from read %p", (void *)uuid);
777
+ if (fio_tls_handshake(uuid, udata))
778
+ return fio_tls_read(uuid, udata, buf, count);
779
+ errno = EWOULDBLOCK;
780
+ return -1;
781
+ }
782
+
783
+ static ssize_t fio_tls_write4handshake(intptr_t uuid, void *udata,
784
+ const void *buf, size_t count) {
785
+ // FIO_LOG_DEBUG("TLS handshake from write %p", (void *)uuid);
786
+ if (fio_tls_handshake(uuid, udata))
787
+ return fio_tls_write(uuid, udata, buf, count);
788
+ errno = EWOULDBLOCK;
789
+ return -1;
790
+ }
791
+
792
+ static ssize_t fio_tls_flush4handshake(intptr_t uuid, void *udata) {
793
+ // FIO_LOG_DEBUG("TLS handshake from flush %p", (void *)uuid);
794
+ if (fio_tls_handshake(uuid, udata)) {
795
+ return fio_tls_flush(uuid, udata);
796
+ }
797
+ errno = 0;
798
+ return 1;
799
+ }
800
+ static fio_rw_hook_s FIO_TLS_HANDSHAKE_HOOKS = {
801
+ .read = fio_tls_read4handshake,
802
+ .write = fio_tls_write4handshake,
803
+ .before_close = fio_tls_before_close,
804
+ .flush = fio_tls_flush4handshake,
805
+ .cleanup = fio_tls_cleanup,
806
+ };
807
+ static inline void fio_tls_attach2uuid(intptr_t uuid, fio_tls_s *tls,
808
+ void *udata, uint8_t is_server) {
809
+ fio_atomic_add(&tls->ref, 1);
810
+ /* create SSL connection context from global context */
811
+ fio_tls_connection_s *c = malloc(sizeof(*c));
812
+ FIO_ASSERT_ALLOC(c);
813
+ *c = (fio_tls_connection_s){
814
+ .alpn_arg = udata,
815
+ .tls = tls,
816
+ .uuid = uuid,
817
+ .ssl = SSL_new(tls->ctx),
818
+ .is_server = is_server,
819
+ .alpn_ok = 0,
820
+ };
821
+ FIO_ASSERT_ALLOC(c->ssl);
822
+ /* set facil.io data in the SSL object */
823
+ SSL_set_ex_data(c->ssl, 0, (void *)c);
824
+ /* attach socket - TODO: Switch to BIO socket */
825
+ BIO *bio = BIO_new_socket(fio_uuid2fd(uuid), 0);
826
+ BIO_up_ref(bio);
827
+ SSL_set0_rbio(c->ssl, bio);
828
+ SSL_set0_wbio(c->ssl, bio);
829
+ /* set RW hooks */
830
+ fio_rw_hook_set(uuid, &FIO_TLS_HANDSHAKE_HOOKS, c);
831
+ if (is_server) {
832
+ /* Server mode (accept) */
833
+ FIO_LOG_DEBUG("Attaching TLS read/write hook for %p (server mode).",
834
+ (void *)uuid);
835
+ SSL_set_accept_state(c->ssl);
836
+ } else {
837
+ /* Client mode (connect) */
838
+ FIO_LOG_DEBUG("Attaching TLS read/write hook for %p (client mode).",
839
+ (void *)uuid);
840
+ SSL_set_connect_state(c->ssl);
841
+ }
842
+ fio_force_event(uuid, FIO_EVENT_ON_READY);
843
+ }
844
+
845
+ /* *****************************************************************************
846
+ SSL/TLS API implementation - this can be pretty much used as is...
847
+ ***************************************************************************** */
848
+
849
+ /**
850
+ * Creates a new SSL/TLS context / settings object with a default certificate
851
+ * (if any).
852
+ */
853
+ fio_tls_s *FIO_TLS_WEAK fio_tls_new(const char *server_name, const char *cert,
854
+ const char *key, const char *pk_password) {
855
+ REQUIRE_LIBRARY();
856
+ fio_tls_s *tls = calloc(sizeof(*tls), 1);
857
+ tls->ref = 1;
858
+ fio_tls_cert_add(tls, server_name, key, cert, pk_password);
859
+ return tls;
860
+ }
861
+
862
+ /**
863
+ * Adds a certificate a new SSL/TLS context / settings object.
864
+ */
865
+ void FIO_TLS_WEAK fio_tls_cert_add(fio_tls_s *tls, const char *server_name,
866
+ const char *cert, const char *key,
867
+ const char *pk_password) {
868
+ REQUIRE_LIBRARY();
869
+ cert_s c = {
870
+ .private_key = FIO_STR_INIT,
871
+ .public_key = FIO_STR_INIT,
872
+ .password = FIO_STR_INIT_STATIC2(pk_password,
873
+ (pk_password ? strlen(pk_password) : 0)),
874
+ };
875
+ if (key && cert) {
876
+ if (fio_str_readfile(&c.private_key, key, 0, 0).data == NULL)
877
+ goto file_missing;
878
+ if (fio_str_readfile(&c.public_key, cert, 0, 0).data == NULL)
879
+ goto file_missing;
880
+ cert_ary_push(&tls->sni, c);
881
+ } else if (server_name) {
882
+ /* Self-Signed TLS Certificates */
883
+ c.private_key = FIO_STR_INIT_STATIC(server_name);
884
+ cert_ary_push(&tls->sni, c);
885
+ }
886
+ fio_tls_cert_destroy(&c);
887
+ fio_tls_build_context(tls);
888
+ return;
889
+ file_missing:
890
+ FIO_LOG_FATAL("TLS certificate file missing for either %s or %s or both.",
891
+ key, cert);
892
+ exit(-1);
893
+ }
894
+
895
+ /**
896
+ * Adds an ALPN protocol callback to the SSL/TLS context.
897
+ *
898
+ * The first protocol added will act as the default protocol to be selected.
899
+ *
900
+ * The callback should accept the `uuid`, the user data pointer passed to
901
+ * either `fio_tls_accept` or `fio_tls_connect` (here: `udata_connetcion`) and
902
+ * the user data pointer passed to the `fio_tls_alpn_add` function
903
+ * (`udata_tls`).
904
+ *
905
+ * The `on_cleanup` callback will be called when the TLS object is destroyed
906
+ * (or `fio_tls_alpn_add` is called again with the same protocol name). The
907
+ * `udata_tls` argumrnt will be passed along, as is, to the callback (if set).
908
+ *
909
+ * Except for the `tls` and `protocol_name` arguments, all arguments can be
910
+ * NULL.
911
+ */
912
+ void FIO_TLS_WEAK fio_tls_alpn_add(
913
+ fio_tls_s *tls, const char *protocol_name,
914
+ void (*on_selected)(intptr_t uuid, void *udata_connection, void *udata_tls),
915
+ void *udata_tls, void (*on_cleanup)(void *udata_tls)) {
916
+ REQUIRE_LIBRARY();
917
+ alpn_add(tls, protocol_name, on_selected, udata_tls, on_cleanup);
918
+ fio_tls_build_context(tls);
919
+ }
920
+
921
+ /**
922
+ * Returns the number of registered ALPN protocol names.
923
+ *
924
+ * This could be used when deciding if protocol selection should be delegated
925
+ * to the ALPN mechanism, or whether a protocol should be immediately
926
+ * assigned.
927
+ *
928
+ * If no ALPN protocols are registered, zero (0) is returned.
929
+ */
930
+ uintptr_t FIO_TLS_WEAK fio_tls_alpn_count(fio_tls_s *tls) {
931
+ return tls ? alpn_list_count(&tls->alpn) : 0;
932
+ }
933
+
934
+ /**
935
+ * Adds a certificate to the "trust" list, which automatically adds a peer
936
+ * verification requirement.
937
+ *
938
+ * fio_tls_trust(tls, "google-ca.pem" );
939
+ */
940
+ void FIO_TLS_WEAK fio_tls_trust(fio_tls_s *tls, const char *public_cert_file) {
941
+ REQUIRE_LIBRARY();
942
+ trust_s c = {
943
+ .pem = FIO_STR_INIT,
944
+ };
945
+ if (!public_cert_file)
946
+ return;
947
+ if (fio_str_readfile(&c.pem, public_cert_file, 0, 0).data == NULL)
948
+ goto file_missing;
949
+ trust_ary_push(&tls->trust, c);
950
+ fio_tls_trust_destroy(&c);
951
+ fio_tls_build_context(tls);
952
+ return;
953
+ file_missing:
954
+ FIO_LOG_FATAL("TLS certificate file missing for %s ", public_cert_file);
955
+ exit(-1);
956
+ }
957
+
958
+ /**
959
+ * Establishes an SSL/TLS connection as an SSL/TLS Server, using the specified
960
+ * context / settings object.
961
+ *
962
+ * The `uuid` should be a socket UUID that is already connected to a peer
963
+ * (i.e., the result of `fio_accept`).
964
+ *
965
+ * The `udata` is an opaque user data pointer that is passed along to the
966
+ * protocol selected (if any protocols were added using `fio_tls_alpn_add`).
967
+ */
968
+ void FIO_TLS_WEAK fio_tls_accept(intptr_t uuid, fio_tls_s *tls, void *udata) {
969
+ REQUIRE_LIBRARY();
970
+ fio_tls_attach2uuid(uuid, tls, udata, 1);
971
+ }
972
+
973
+ /**
974
+ * Establishes an SSL/TLS connection as an SSL/TLS Client, using the specified
975
+ * context / settings object.
976
+ *
977
+ * The `uuid` should be a socket UUID that is already connected to a peer
978
+ * (i.e., one received by a `fio_connect` specified callback `on_connect`).
979
+ *
980
+ * The `udata` is an opaque user data pointer that is passed along to the
981
+ * protocol selected (if any protocols were added using `fio_tls_alpn_add`).
982
+ */
983
+ void FIO_TLS_WEAK fio_tls_connect(intptr_t uuid, fio_tls_s *tls, void *udata) {
984
+ REQUIRE_LIBRARY();
985
+ fio_tls_attach2uuid(uuid, tls, udata, 0);
986
+ }
987
+
988
+ /**
989
+ * Increase the reference count for the TLS object.
990
+ *
991
+ * Decrease with `fio_tls_destroy`.
992
+ */
993
+ void FIO_TLS_WEAK fio_tls_dup(fio_tls_s *tls) { fio_atomic_add(&tls->ref, 1); }
994
+
995
+ /**
996
+ * Destroys the SSL/TLS context / settings object and frees any related
997
+ * resources / memory.
998
+ */
999
+ void FIO_TLS_WEAK fio_tls_destroy(fio_tls_s *tls) {
1000
+ if (!tls)
1001
+ return;
1002
+ REQUIRE_LIBRARY();
1003
+ if (fio_atomic_sub(&tls->ref, 1))
1004
+ return;
1005
+ fio_tls_destroy_context(tls);
1006
+ alpn_list_free(&tls->alpn);
1007
+ cert_ary_free(&tls->sni);
1008
+ free(tls);
1009
+ }
1010
+
1011
+ #endif /* Library compiler flags */