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,649 @@
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 is the default
18
+ * implementation that will be used when no SSL/TLS library is available...
19
+ *
20
+ * ... without modification, this implementation crashes the program.
21
+ *
22
+ * The implementation can be USED AS A TEMPLATE for future implementations.
23
+ *
24
+ * This implementation is optimized for ease of development rather than memory
25
+ * consumption.
26
+ */
27
+ #include "fio_tls.h"
28
+
29
+ #if !defined(FIO_TLS_FOUND) /* Library compiler flags */
30
+
31
+ #define REQUIRE_LIBRARY()
32
+ #define FIO_TLS_WEAK
33
+
34
+ /* TODO: delete me! */
35
+ #undef FIO_TLS_WEAK
36
+ #define FIO_TLS_WEAK __attribute__((weak))
37
+ #if !FIO_IGNORE_TLS_IF_MISSING
38
+ #undef REQUIRE_LIBRARY
39
+ #define REQUIRE_LIBRARY() \
40
+ FIO_LOG_FATAL("No supported SSL/TLS library available."); \
41
+ exit(-1);
42
+ #endif
43
+
44
+ #ifndef FIO_TLS_TIMEOUT
45
+ #define FIO_TLS_TIMEOUT 4
46
+ #endif
47
+
48
+ /* STOP deleting after this line */
49
+
50
+ /* *****************************************************************************
51
+ The SSL/TLS helper data types (can be left as is)
52
+ ***************************************************************************** */
53
+ #define FIO_INCLUDE_STR 1
54
+ #define FIO_FORCE_MALLOC_TMP 1
55
+ #include <fio.h>
56
+
57
+ typedef struct {
58
+ fio_str_s private_key;
59
+ fio_str_s public_key;
60
+ fio_str_s password;
61
+ } cert_s;
62
+
63
+ static inline int fio_tls_cert_cmp(const cert_s *dest, const cert_s *src) {
64
+ return fio_str_iseq(&dest->private_key, &src->private_key);
65
+ }
66
+ static inline void fio_tls_cert_copy(cert_s *dest, cert_s *src) {
67
+ *dest = (cert_s){
68
+ .private_key = FIO_STR_INIT,
69
+ .public_key = FIO_STR_INIT,
70
+ .password = FIO_STR_INIT,
71
+ };
72
+ fio_str_concat(&dest->private_key, &src->private_key);
73
+ fio_str_concat(&dest->public_key, &src->public_key);
74
+ fio_str_concat(&dest->password, &src->password);
75
+ }
76
+ static inline void fio_tls_cert_destroy(cert_s *obj) {
77
+ fio_str_free(&obj->private_key);
78
+ fio_str_free(&obj->public_key);
79
+ fio_str_free(&obj->password);
80
+ }
81
+
82
+ #define FIO_ARY_NAME cert_ary
83
+ #define FIO_ARY_TYPE cert_s
84
+ #define FIO_ARY_COMPARE(k1, k2) (fio_tls_cert_cmp(&(k1), &(k2)))
85
+ #define FIO_ARY_COPY(dest, obj) fio_tls_cert_copy(&(dest), &(obj))
86
+ #define FIO_ARY_DESTROY(key) fio_tls_cert_destroy(&(key))
87
+ #define FIO_FORCE_MALLOC_TMP 1
88
+ #include <fio.h>
89
+
90
+ typedef struct {
91
+ fio_str_s pem;
92
+ } trust_s;
93
+
94
+ static inline int fio_tls_trust_cmp(const trust_s *dest, const trust_s *src) {
95
+ return fio_str_iseq(&dest->pem, &src->pem);
96
+ }
97
+ static inline void fio_tls_trust_copy(trust_s *dest, trust_s *src) {
98
+ *dest = (trust_s){
99
+ .pem = FIO_STR_INIT,
100
+ };
101
+ fio_str_concat(&dest->pem, &src->pem);
102
+ }
103
+ static inline void fio_tls_trust_destroy(trust_s *obj) {
104
+ fio_str_free(&obj->pem);
105
+ }
106
+
107
+ #define FIO_ARY_NAME trust_ary
108
+ #define FIO_ARY_TYPE trust_s
109
+ #define FIO_ARY_COMPARE(k1, k2) (fio_tls_trust_cmp(&(k1), &(k2)))
110
+ #define FIO_ARY_COPY(dest, obj) fio_tls_trust_copy(&(dest), &(obj))
111
+ #define FIO_ARY_DESTROY(key) fio_tls_trust_destroy(&(key))
112
+ #define FIO_FORCE_MALLOC_TMP 1
113
+ #include <fio.h>
114
+
115
+ typedef struct {
116
+ fio_str_s name; /* fio_str_s provides cache locality for small strings */
117
+ void (*on_selected)(intptr_t uuid, void *udata_connection, void *udata_tls);
118
+ void *udata_tls;
119
+ void (*on_cleanup)(void *udata_tls);
120
+ } alpn_s;
121
+
122
+ static inline int fio_alpn_cmp(const alpn_s *dest, const alpn_s *src) {
123
+ return fio_str_iseq(&dest->name, &src->name);
124
+ }
125
+ static inline void fio_alpn_copy(alpn_s *dest, alpn_s *src) {
126
+ *dest = (alpn_s){
127
+ .name = FIO_STR_INIT,
128
+ .on_selected = src->on_selected,
129
+ .udata_tls = src->udata_tls,
130
+ .on_cleanup = src->on_cleanup,
131
+ };
132
+ fio_str_concat(&dest->name, &src->name);
133
+ }
134
+ static inline void fio_alpn_destroy(alpn_s *obj) {
135
+ if (obj->on_cleanup)
136
+ obj->on_cleanup(obj->udata_tls);
137
+ fio_str_free(&obj->name);
138
+ }
139
+
140
+ #define FIO_SET_NAME alpn_list
141
+ #define FIO_SET_OBJ_TYPE alpn_s
142
+ #define FIO_SET_OBJ_COMPARE(k1, k2) fio_alpn_cmp(&(k1), &(k2))
143
+ #define FIO_SET_OBJ_COPY(dest, obj) fio_alpn_copy(&(dest), &(obj))
144
+ #define FIO_SET_OBJ_DESTROY(key) fio_alpn_destroy(&(key))
145
+ #define FIO_FORCE_MALLOC_TMP 1
146
+ #include <fio.h>
147
+
148
+ /* *****************************************************************************
149
+ The SSL/TLS Context type
150
+ ***************************************************************************** */
151
+
152
+ /** An opaque type used for the SSL/TLS functions. */
153
+ struct fio_tls_s {
154
+ size_t ref; /* Reference counter, to guards the ALPN registry */
155
+ alpn_list_s alpn; /* ALPN is the name for the protocol selection extension */
156
+
157
+ /*** the next two components could be optimized away with tweaking stuff ***/
158
+
159
+ cert_ary_s sni; /* SNI (server name extension) stores ID certificates */
160
+ trust_ary_s trust; /* Trusted certificate registry (peer verification) */
161
+
162
+ /************ TODO: implementation data fields go here ******************/
163
+ };
164
+
165
+ /* *****************************************************************************
166
+ ALPN Helpers
167
+ ***************************************************************************** */
168
+
169
+ /** Returns a pointer to the ALPN data (callback, etc') IF exists in the TLS. */
170
+ FIO_FUNC inline alpn_s *alpn_find(fio_tls_s *tls, char *name, size_t len) {
171
+ alpn_s tmp = {.name = FIO_STR_INIT_STATIC2(name, len)};
172
+ alpn_list__map_s_ *pos =
173
+ alpn_list__find_map_pos_(&tls->alpn, fio_str_hash(&tmp.name), tmp);
174
+ if (!pos || !pos->pos)
175
+ return NULL;
176
+ return &pos->pos->obj;
177
+ }
178
+
179
+ /** Adds an ALPN data object to the ALPN "list" (set) */
180
+ FIO_FUNC inline void alpn_add(
181
+ fio_tls_s *tls, const char *protocol_name,
182
+ void (*on_selected)(intptr_t uuid, void *udata_connection, void *udata_tls),
183
+ void *udata_tls, void (*on_cleanup)(void *udata_tls)) {
184
+ alpn_s tmp = {
185
+ .name = FIO_STR_INIT_STATIC(protocol_name),
186
+ .on_selected = on_selected,
187
+ .udata_tls = udata_tls,
188
+ .on_cleanup = on_cleanup,
189
+ };
190
+ if (fio_str_len(&tmp.name) > 255) {
191
+ FIO_LOG_ERROR("ALPN protocol names are limited to 255 bytes.");
192
+ return;
193
+ }
194
+ alpn_list_overwrite(&tls->alpn, fio_str_hash(&tmp.name), tmp, NULL);
195
+ tmp.on_cleanup = NULL;
196
+ fio_alpn_destroy(&tmp);
197
+ }
198
+
199
+ /** Returns a pointer to the default (first) ALPN object in the TLS (if any). */
200
+ FIO_FUNC inline alpn_s *alpn_default(fio_tls_s *tls) {
201
+ if (!tls || !alpn_list_count(&tls->alpn) || !tls->alpn.ordered)
202
+ return NULL;
203
+ return &tls->alpn.ordered[0].obj;
204
+ }
205
+
206
+ typedef struct {
207
+ alpn_s alpn;
208
+ intptr_t uuid;
209
+ void *udata_connection;
210
+ } alpn_task_s;
211
+
212
+ FIO_FUNC inline void alpn_select___task(void *t_, void *ignr_) {
213
+ alpn_task_s *t = t_;
214
+ if (fio_is_valid(t->uuid))
215
+ t->alpn.on_selected(t->uuid, t->udata_connection, t->alpn.udata_tls);
216
+ fio_free(t);
217
+ (void)ignr_;
218
+ }
219
+
220
+ /** Schedules the ALPN protocol callback. */
221
+ FIO_FUNC inline void alpn_select(alpn_s *alpn, intptr_t uuid,
222
+ void *udata_connection) {
223
+ if (!alpn || !alpn->on_selected)
224
+ return;
225
+ alpn_task_s *t = fio_malloc(sizeof(*t));
226
+ *t = (alpn_task_s){
227
+ .alpn = *alpn,
228
+ .uuid = uuid,
229
+ .udata_connection = udata_connection,
230
+ };
231
+ /* move task out of the socket's lock */
232
+ fio_defer(alpn_select___task, t, NULL);
233
+ }
234
+
235
+ /* *****************************************************************************
236
+ SSL/TLS Context (re)-building - TODO: add implementation details
237
+ ***************************************************************************** */
238
+
239
+ /** Called when the library specific data for the context should be destroyed */
240
+ static void fio_tls_destroy_context(fio_tls_s *tls) {
241
+ /* TODO: Library specific implementation */
242
+ FIO_LOG_DEBUG("destroyed TLS context %p", (void *)tls);
243
+ }
244
+
245
+ /** Called when the library specific data for the context should be built */
246
+ static void fio_tls_build_context(fio_tls_s *tls) {
247
+ fio_tls_destroy_context(tls);
248
+ /* TODO: Library specific implementation */
249
+
250
+ /* Certificates */
251
+ FIO_ARY_FOR(&tls->sni, pos) {
252
+ fio_str_info_s k = fio_str_info(&pos->private_key);
253
+ fio_str_info_s p = fio_str_info(&pos->public_key);
254
+ fio_str_info_s pw = fio_str_info(&pos->password);
255
+ if (p.len && k.len) {
256
+ /* TODO: attache certificate */
257
+ (void)pw;
258
+ } else {
259
+ /* TODO: self signed certificate */
260
+ }
261
+ }
262
+
263
+ /* ALPN Protocols */
264
+ FIO_SET_FOR_LOOP(&tls->alpn, pos) {
265
+ fio_str_info_s name = fio_str_info(&pos->obj.name);
266
+ (void)name;
267
+ // map to pos->callback;
268
+ }
269
+
270
+ /* Peer Verification / Trust */
271
+ if (trust_ary_count(&tls->trust)) {
272
+ /* TODO: enable peer verification */
273
+
274
+ /* TODO: Add each ceriticate in the PEM to the trust "store" */
275
+ FIO_ARY_FOR(&tls->trust, pos) {
276
+ fio_str_info_s pem = fio_str_info(&pos->pem);
277
+ (void)pem;
278
+ }
279
+ }
280
+
281
+ FIO_LOG_DEBUG("(re)built TLS context %p", (void *)tls);
282
+ }
283
+
284
+ /* *****************************************************************************
285
+ SSL/TLS RW Hooks - TODO: add implementation details
286
+ ***************************************************************************** */
287
+
288
+ /* TODO: this is an example implementation - fix for specific library. */
289
+
290
+ #define TLS_BUFFER_LENGTH (1 << 15)
291
+ typedef struct {
292
+ fio_tls_s *tls;
293
+ size_t len;
294
+ uint8_t alpn_ok;
295
+ char buffer[TLS_BUFFER_LENGTH];
296
+ } buffer_s;
297
+
298
+ /**
299
+ * Implement reading from a file descriptor. Should behave like the file
300
+ * system `read` call, including the setup or errno to EAGAIN / EWOULDBLOCK.
301
+ *
302
+ * Note: facil.io library functions MUST NEVER be called by any r/w hook, or a
303
+ * deadlock might occur.
304
+ */
305
+ static ssize_t fio_tls_read(intptr_t uuid, void *udata, void *buf,
306
+ size_t count) {
307
+ ssize_t ret = read(fio_uuid2fd(uuid), buf, count);
308
+ if (ret > 0) {
309
+ FIO_LOG_DEBUG("Read %zd bytes from %p", ret, (void *)uuid);
310
+ }
311
+ return ret;
312
+ (void)udata;
313
+ }
314
+
315
+ /**
316
+ * When implemented, this function will be called to flush any data remaining
317
+ * in the internal buffer.
318
+ *
319
+ * The function should return the number of bytes remaining in the internal
320
+ * buffer (0 is a valid response) or -1 (on error).
321
+ *
322
+ * Note: facil.io library functions MUST NEVER be called by any r/w hook, or a
323
+ * deadlock might occur.
324
+ */
325
+ static ssize_t fio_tls_flush(intptr_t uuid, void *udata) {
326
+ buffer_s *buffer = udata;
327
+ if (!buffer->len) {
328
+ FIO_LOG_DEBUG("Flush empty for %p", (void *)uuid);
329
+ return 0;
330
+ }
331
+ ssize_t r = write(fio_uuid2fd(uuid), buffer->buffer, buffer->len);
332
+ if (r < 0)
333
+ return -1;
334
+ if (r == 0) {
335
+ errno = ECONNRESET;
336
+ return -1;
337
+ }
338
+ size_t len = buffer->len - r;
339
+ if (len)
340
+ memmove(buffer->buffer, buffer->buffer + r, len);
341
+ buffer->len = len;
342
+ FIO_LOG_DEBUG("Sent %zd bytes to %p", r, (void *)uuid);
343
+ return r;
344
+ }
345
+
346
+ /**
347
+ * Implement writing to a file descriptor. Should behave like the file system
348
+ * `write` call.
349
+ *
350
+ * If an internal buffer is implemented and it is full, errno should be set to
351
+ * EWOULDBLOCK and the function should return -1.
352
+ *
353
+ * Note: facil.io library functions MUST NEVER be called by any r/w hook, or a
354
+ * deadlock might occur.
355
+ */
356
+ static ssize_t fio_tls_write(intptr_t uuid, void *udata, const void *buf,
357
+ size_t count) {
358
+ buffer_s *buffer = udata;
359
+ size_t can_copy = TLS_BUFFER_LENGTH - buffer->len;
360
+ if (can_copy > count)
361
+ can_copy = count;
362
+ if (!can_copy)
363
+ goto would_block;
364
+ memcpy(buffer->buffer + buffer->len, buf, can_copy);
365
+ buffer->len += can_copy;
366
+ FIO_LOG_DEBUG("Copied %zu bytes to %p", can_copy, (void *)uuid);
367
+ fio_tls_flush(uuid, udata);
368
+ return can_copy;
369
+ would_block:
370
+ errno = EWOULDBLOCK;
371
+ return -1;
372
+ }
373
+
374
+ /**
375
+ * The `close` callback should close the underlying socket / file descriptor.
376
+ *
377
+ * If the function returns a non-zero value, it will be called again after an
378
+ * attempt to flush the socket and any pending outgoing buffer.
379
+ *
380
+ * Note: facil.io library functions MUST NEVER be called by any r/w hook, or a
381
+ * deadlock might occur.
382
+ * */
383
+ static ssize_t fio_tls_before_close(intptr_t uuid, void *udata) {
384
+ FIO_LOG_DEBUG("The `before_close` callback was called for %p", (void *)uuid);
385
+ return 1;
386
+ (void)udata;
387
+ }
388
+ /**
389
+ * Called to perform cleanup after the socket was closed.
390
+ * */
391
+ static void fio_tls_cleanup(void *udata) {
392
+ buffer_s *buffer = udata;
393
+ /* make sure the ALPN callback was called, just in case cleanup is required */
394
+ if (!buffer->alpn_ok) {
395
+ alpn_select(alpn_default(buffer->tls), -1, NULL /* ALPN udata */);
396
+ }
397
+ fio_tls_destroy(buffer->tls); /* manage reference count */
398
+ fio_free(udata);
399
+ }
400
+
401
+ static fio_rw_hook_s FIO_TLS_HOOKS = {
402
+ .read = fio_tls_read,
403
+ .write = fio_tls_write,
404
+ .before_close = fio_tls_before_close,
405
+ .flush = fio_tls_flush,
406
+ .cleanup = fio_tls_cleanup,
407
+ };
408
+
409
+ static size_t fio_tls_handshake(intptr_t uuid, void *udata) {
410
+ /*TODO: test for handshake completion */
411
+ if (0 /*handshake didn't complete */)
412
+ return 0;
413
+ if (fio_rw_hook_replace_unsafe(uuid, &FIO_TLS_HOOKS, udata) == 0) {
414
+ FIO_LOG_DEBUG("Completed TLS handshake for %p", (void *)uuid);
415
+ /*
416
+ * make sure the connection is re-added to the reactor...
417
+ * in case, while waiting for ALPN, it was suspended for missing a protocol.
418
+ */
419
+ fio_force_event(uuid, FIO_EVENT_ON_DATA);
420
+ } else {
421
+ FIO_LOG_DEBUG("Something went wrong during TLS handshake for %p",
422
+ (void *)uuid);
423
+ }
424
+ return 1;
425
+ }
426
+
427
+ static ssize_t fio_tls_read4handshake(intptr_t uuid, void *udata, void *buf,
428
+ size_t count) {
429
+ FIO_LOG_DEBUG("TLS handshake from read %p", (void *)uuid);
430
+ if (fio_tls_handshake(uuid, udata))
431
+ return fio_tls_read(uuid, udata, buf, count);
432
+ errno = EWOULDBLOCK;
433
+ return -1;
434
+ }
435
+
436
+ static ssize_t fio_tls_write4handshake(intptr_t uuid, void *udata,
437
+ const void *buf, size_t count) {
438
+ FIO_LOG_DEBUG("TLS handshake from write %p", (void *)uuid);
439
+ if (fio_tls_handshake(uuid, udata))
440
+ return fio_tls_write(uuid, udata, buf, count);
441
+ errno = EWOULDBLOCK;
442
+ return -1;
443
+ }
444
+
445
+ static ssize_t fio_tls_flush4handshake(intptr_t uuid, void *udata) {
446
+ FIO_LOG_DEBUG("TLS handshake from flush %p", (void *)uuid);
447
+ if (fio_tls_handshake(uuid, udata))
448
+ return fio_tls_flush(uuid, udata);
449
+ /* TODO: return a positive value only if handshake requires a write */
450
+ return 1;
451
+ }
452
+ static fio_rw_hook_s FIO_TLS_HANDSHAKE_HOOKS = {
453
+ .read = fio_tls_read4handshake,
454
+ .write = fio_tls_write4handshake,
455
+ .before_close = fio_tls_before_close,
456
+ .flush = fio_tls_flush4handshake,
457
+ .cleanup = fio_tls_cleanup,
458
+ };
459
+
460
+ static inline void fio_tls_attach2uuid(intptr_t uuid, fio_tls_s *tls,
461
+ void *udata, uint8_t is_server) {
462
+ fio_atomic_add(&tls->ref, 1); /* manage reference count */
463
+ /* TODO: this is only an example implementation - fix for specific library */
464
+ if (is_server) {
465
+ /* Server mode (accept) */
466
+ FIO_LOG_DEBUG("Attaching TLS read/write hook for %p (server mode).",
467
+ (void *)uuid);
468
+ } else {
469
+ /* Client mode (connect) */
470
+ FIO_LOG_DEBUG("Attaching TLS read/write hook for %p (client mode).",
471
+ (void *)uuid);
472
+ }
473
+ /* common implementation (TODO) */
474
+ buffer_s *connection_data = fio_malloc(sizeof(*connection_data));
475
+ FIO_ASSERT_ALLOC(connection_data);
476
+ fio_rw_hook_set(uuid, &FIO_TLS_HANDSHAKE_HOOKS,
477
+ connection_data); /* 32Kb buffer */
478
+ alpn_select(alpn_default(tls), uuid, udata);
479
+ connection_data->alpn_ok = 1;
480
+ }
481
+
482
+ /* *****************************************************************************
483
+ SSL/TLS API implementation - this can be pretty much used as is...
484
+ ***************************************************************************** */
485
+
486
+ /**
487
+ * Creates a new SSL/TLS context / settings object with a default certificate
488
+ * (if any).
489
+ */
490
+ fio_tls_s *FIO_TLS_WEAK fio_tls_new(const char *server_name, const char *cert,
491
+ const char *key, const char *pk_password) {
492
+ REQUIRE_LIBRARY();
493
+ fio_tls_s *tls = calloc(sizeof(*tls), 1);
494
+ tls->ref = 1;
495
+ fio_tls_cert_add(tls, server_name, key, cert, pk_password);
496
+ return tls;
497
+ }
498
+
499
+ /**
500
+ * Adds a certificate a new SSL/TLS context / settings object.
501
+ */
502
+ void FIO_TLS_WEAK fio_tls_cert_add(fio_tls_s *tls, const char *server_name,
503
+ const char *cert, const char *key,
504
+ const char *pk_password) {
505
+ REQUIRE_LIBRARY();
506
+ cert_s c = {
507
+ .private_key = FIO_STR_INIT,
508
+ .public_key = FIO_STR_INIT,
509
+ .password = FIO_STR_INIT_STATIC2(pk_password,
510
+ (pk_password ? strlen(pk_password) : 0)),
511
+ };
512
+ if (key && cert) {
513
+ if (fio_str_readfile(&c.private_key, key, 0, 0).data == NULL)
514
+ goto file_missing;
515
+ if (fio_str_readfile(&c.public_key, cert, 0, 0).data == NULL)
516
+ goto file_missing;
517
+ cert_ary_push(&tls->sni, c);
518
+ } else if (server_name) {
519
+ /* Self-Signed TLS Certificates */
520
+ c.private_key = FIO_STR_INIT_STATIC(server_name);
521
+ cert_ary_push(&tls->sni, c);
522
+ }
523
+ fio_tls_cert_destroy(&c);
524
+ fio_tls_build_context(tls);
525
+ return;
526
+ file_missing:
527
+ FIO_LOG_FATAL("TLS certificate file missing for either %s or %s or both.",
528
+ key, cert);
529
+ exit(-1);
530
+ }
531
+
532
+ /**
533
+ * Adds an ALPN protocol callback to the SSL/TLS context.
534
+ *
535
+ * The first protocol added will act as the default protocol to be selected.
536
+ *
537
+ * The callback should accept the `uuid`, the user data pointer passed to either
538
+ * `fio_tls_accept` or `fio_tls_connect` (here: `udata_connetcion`) and the user
539
+ * data pointer passed to the `fio_tls_alpn_add` function (`udata_tls`).
540
+ *
541
+ * The `on_cleanup` callback will be called when the TLS object is destroyed (or
542
+ * `fio_tls_alpn_add` is called again with the same protocol name). The
543
+ * `udata_tls` argumrnt will be passed along, as is, to the callback (if set).
544
+ *
545
+ * Except for the `tls` and `protocol_name` arguments, all arguments can be
546
+ * NULL.
547
+ */
548
+ void FIO_TLS_WEAK fio_tls_alpn_add(
549
+ fio_tls_s *tls, const char *protocol_name,
550
+ void (*on_selected)(intptr_t uuid, void *udata_connection, void *udata_tls),
551
+ void *udata_tls, void (*on_cleanup)(void *udata_tls)) {
552
+ REQUIRE_LIBRARY();
553
+ alpn_add(tls, protocol_name, on_selected, udata_tls, on_cleanup);
554
+ fio_tls_build_context(tls);
555
+ }
556
+
557
+ /**
558
+ * Returns the number of registered ALPN protocol names.
559
+ *
560
+ * This could be used when deciding if protocol selection should be delegated to
561
+ * the ALPN mechanism, or whether a protocol should be immediately assigned.
562
+ *
563
+ * If no ALPN protocols are registered, zero (0) is returned.
564
+ */
565
+ uintptr_t FIO_TLS_WEAK fio_tls_alpn_count(fio_tls_s *tls) {
566
+ return tls ? alpn_list_count(&tls->alpn) : 0;
567
+ }
568
+
569
+ /**
570
+ * Adds a certificate to the "trust" list, which automatically adds a peer
571
+ * verification requirement.
572
+ *
573
+ * fio_tls_trust(tls, "google-ca.pem" );
574
+ */
575
+ void FIO_TLS_WEAK fio_tls_trust(fio_tls_s *tls, const char *public_cert_file) {
576
+ REQUIRE_LIBRARY();
577
+ trust_s c = {
578
+ .pem = FIO_STR_INIT,
579
+ };
580
+ if (!public_cert_file)
581
+ return;
582
+ if (fio_str_readfile(&c.pem, public_cert_file, 0, 0).data == NULL)
583
+ goto file_missing;
584
+ trust_ary_push(&tls->trust, c);
585
+ fio_tls_trust_destroy(&c);
586
+ fio_tls_build_context(tls);
587
+ return;
588
+ file_missing:
589
+ FIO_LOG_FATAL("TLS certificate file missing for %s ", public_cert_file);
590
+ exit(-1);
591
+ }
592
+
593
+ /**
594
+ * Establishes an SSL/TLS connection as an SSL/TLS Server, using the specified
595
+ * context / settings object.
596
+ *
597
+ * The `uuid` should be a socket UUID that is already connected to a peer (i.e.,
598
+ * the result of `fio_accept`).
599
+ *
600
+ * The `udata` is an opaque user data pointer that is passed along to the
601
+ * protocol selected (if any protocols were added using `fio_tls_alpn_add`).
602
+ */
603
+ void FIO_TLS_WEAK fio_tls_accept(intptr_t uuid, fio_tls_s *tls, void *udata) {
604
+ REQUIRE_LIBRARY();
605
+ fio_timeout_set(uuid, FIO_TLS_TIMEOUT);
606
+ fio_tls_attach2uuid(uuid, tls, udata, 1);
607
+ }
608
+
609
+ /**
610
+ * Establishes an SSL/TLS connection as an SSL/TLS Client, using the specified
611
+ * context / settings object.
612
+ *
613
+ * The `uuid` should be a socket UUID that is already connected to a peer (i.e.,
614
+ * one received by a `fio_connect` specified callback `on_connect`).
615
+ *
616
+ * The `udata` is an opaque user data pointer that is passed along to the
617
+ * protocol selected (if any protocols were added using `fio_tls_alpn_add`).
618
+ */
619
+ void FIO_TLS_WEAK fio_tls_connect(intptr_t uuid, fio_tls_s *tls, void *udata) {
620
+ REQUIRE_LIBRARY();
621
+ fio_tls_attach2uuid(uuid, tls, udata, 0);
622
+ }
623
+
624
+ /**
625
+ * Increase the reference count for the TLS object.
626
+ *
627
+ * Decrease with `fio_tls_destroy`.
628
+ */
629
+ void FIO_TLS_WEAK fio_tls_dup(fio_tls_s *tls) { fio_atomic_add(&tls->ref, 1); }
630
+
631
+ /**
632
+ * Destroys the SSL/TLS context / settings object and frees any related
633
+ * resources / memory.
634
+ */
635
+ void FIO_TLS_WEAK fio_tls_destroy(fio_tls_s *tls) {
636
+ if (!tls)
637
+ return;
638
+ REQUIRE_LIBRARY();
639
+ if (fio_atomic_sub(&tls->ref, 1))
640
+ return;
641
+ fio_tls_destroy_context(tls);
642
+ alpn_list_free(&tls->alpn);
643
+ cert_ary_free(&tls->sni);
644
+ trust_ary_free(&tls->trust);
645
+ free(tls);
646
+ }
647
+
648
+ #endif /* Library compiler flags */
649
+ #endif