isomorfeus-iodine 0.7.44

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