iodine 0.7.16 → 0.7.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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