trilogy 2.0.0

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

Potentially problematic release.


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

Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +74 -0
  4. data/Rakefile +18 -0
  5. data/ext/trilogy-ruby/cast.c +272 -0
  6. data/ext/trilogy-ruby/cext.c +933 -0
  7. data/ext/trilogy-ruby/extconf.rb +16 -0
  8. data/ext/trilogy-ruby/inc/trilogy/blocking.h +163 -0
  9. data/ext/trilogy-ruby/inc/trilogy/buffer.h +64 -0
  10. data/ext/trilogy-ruby/inc/trilogy/builder.h +161 -0
  11. data/ext/trilogy-ruby/inc/trilogy/charset.h +277 -0
  12. data/ext/trilogy-ruby/inc/trilogy/client.h +546 -0
  13. data/ext/trilogy-ruby/inc/trilogy/error.h +43 -0
  14. data/ext/trilogy-ruby/inc/trilogy/packet_parser.h +34 -0
  15. data/ext/trilogy-ruby/inc/trilogy/protocol.h +756 -0
  16. data/ext/trilogy-ruby/inc/trilogy/reader.h +212 -0
  17. data/ext/trilogy-ruby/inc/trilogy/socket.h +111 -0
  18. data/ext/trilogy-ruby/inc/trilogy/vendor/curl_hostcheck.h +29 -0
  19. data/ext/trilogy-ruby/inc/trilogy/vendor/openssl_hostname_validation.h +51 -0
  20. data/ext/trilogy-ruby/inc/trilogy.h +8 -0
  21. data/ext/trilogy-ruby/src/blocking.c +241 -0
  22. data/ext/trilogy-ruby/src/buffer.c +60 -0
  23. data/ext/trilogy-ruby/src/builder.c +198 -0
  24. data/ext/trilogy-ruby/src/charset.c +212 -0
  25. data/ext/trilogy-ruby/src/client.c +728 -0
  26. data/ext/trilogy-ruby/src/error.c +17 -0
  27. data/ext/trilogy-ruby/src/packet_parser.c +140 -0
  28. data/ext/trilogy-ruby/src/protocol.c +676 -0
  29. data/ext/trilogy-ruby/src/reader.c +244 -0
  30. data/ext/trilogy-ruby/src/socket.c +623 -0
  31. data/ext/trilogy-ruby/src/vendor/curl_hostcheck.c +206 -0
  32. data/ext/trilogy-ruby/src/vendor/openssl_hostname_validation.c +175 -0
  33. data/ext/trilogy-ruby/trilogy-ruby.h +36 -0
  34. data/lib/trilogy/version.rb +3 -0
  35. data/lib/trilogy.rb +61 -0
  36. data/trilogy.gemspec +27 -0
  37. metadata +106 -0
@@ -0,0 +1,623 @@
1
+ #include <netdb.h>
2
+ #include <netinet/tcp.h>
3
+ #include <poll.h>
4
+ #include <sys/socket.h>
5
+ #include <sys/types.h>
6
+ #include <sys/un.h>
7
+
8
+ #include <errno.h>
9
+ #include <fcntl.h>
10
+ #include <stdlib.h>
11
+ #include <unistd.h>
12
+
13
+ #include "trilogy/error.h"
14
+ #include "trilogy/socket.h"
15
+
16
+ #if OPENSSL_VERSION_NUMBER < 0x1000200fL
17
+ #include "trilogy/vendor/openssl_hostname_validation.h"
18
+ #endif
19
+
20
+ struct trilogy_sock {
21
+ trilogy_sock_t base;
22
+ struct addrinfo *addr;
23
+ int fd;
24
+ SSL *ssl;
25
+ };
26
+
27
+ static int _cb_raw_fd(trilogy_sock_t *_sock)
28
+ {
29
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
30
+ return sock->fd;
31
+ }
32
+
33
+ static int _cb_wait(trilogy_sock_t *_sock, trilogy_wait_t wait)
34
+ {
35
+ struct pollfd pfd = {.fd = trilogy_sock_fd(_sock)};
36
+
37
+ switch (wait) {
38
+ case TRILOGY_WAIT_HANDSHAKE:
39
+ case TRILOGY_WAIT_READ:
40
+ pfd.events = POLLIN;
41
+ break;
42
+ case TRILOGY_WAIT_WRITE:
43
+ pfd.events = POLLOUT;
44
+ break;
45
+ default:
46
+ return TRILOGY_ERR;
47
+ }
48
+
49
+ while (1) {
50
+ int rc = poll(&pfd, 1, -1);
51
+
52
+ if (rc < 0) {
53
+ if (errno == EINTR) {
54
+ continue;
55
+ }
56
+ return TRILOGY_SYSERR;
57
+ }
58
+
59
+ return TRILOGY_OK;
60
+ }
61
+ }
62
+
63
+ static ssize_t _cb_raw_read(trilogy_sock_t *_sock, void *buf, size_t nread)
64
+ {
65
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
66
+ ssize_t data_read = read(sock->fd, buf, nread);
67
+ if (data_read < 0) {
68
+ return (ssize_t)TRILOGY_SYSERR;
69
+ }
70
+ return data_read;
71
+ }
72
+
73
+ static ssize_t _cb_raw_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite)
74
+ {
75
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
76
+ ssize_t data_written = write(sock->fd, buf, nwrite);
77
+ if (data_written < 0) {
78
+ return (ssize_t)TRILOGY_SYSERR;
79
+ }
80
+ return data_written;
81
+ }
82
+
83
+ static int _cb_raw_close(trilogy_sock_t *_sock)
84
+ {
85
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
86
+ int rc = 0;
87
+ if (sock->fd != -1) {
88
+ rc = close(sock->fd);
89
+ }
90
+ if (sock->addr) {
91
+ freeaddrinfo(sock->addr);
92
+ }
93
+
94
+ free(sock->base.opts.hostname);
95
+ free(sock->base.opts.path);
96
+ free(sock->base.opts.database);
97
+ free(sock->base.opts.username);
98
+ free(sock->base.opts.password);
99
+ free(sock->base.opts.ssl_ca);
100
+ free(sock->base.opts.ssl_capath);
101
+ free(sock->base.opts.ssl_cert);
102
+ free(sock->base.opts.ssl_cipher);
103
+ free(sock->base.opts.ssl_crl);
104
+ free(sock->base.opts.ssl_crlpath);
105
+ free(sock->base.opts.ssl_key);
106
+ free(sock->base.opts.tls_ciphersuites);
107
+
108
+ free(sock);
109
+ return rc;
110
+ }
111
+
112
+ static int _cb_raw_shutdown(trilogy_sock_t *_sock) { return shutdown(trilogy_sock_fd(_sock), SHUT_RDWR); }
113
+
114
+ static int set_nonblocking_fd(int sock)
115
+ {
116
+ int flags = fcntl(sock, F_GETFL, 0);
117
+
118
+ if (flags < 0) {
119
+ return TRILOGY_SYSERR;
120
+ }
121
+
122
+ if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
123
+ return TRILOGY_SYSERR;
124
+ }
125
+
126
+ return TRILOGY_OK;
127
+ }
128
+
129
+ static int raw_connect_internal(struct trilogy_sock *sock, const struct addrinfo *ai)
130
+ {
131
+ int sockerr;
132
+ socklen_t sockerr_len = sizeof(sockerr);
133
+
134
+ sock->fd = socket(ai->ai_family, SOCK_STREAM, ai->ai_protocol);
135
+ if (sock->fd < 0) {
136
+ return TRILOGY_SYSERR;
137
+ }
138
+
139
+ if (sock->base.opts.keepalive_enabled) {
140
+ int flags = 1;
141
+ if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0) {
142
+ goto fail;
143
+ }
144
+ #ifdef TCP_KEEPIDLE
145
+ if (sock->base.opts.keepalive_idle > 0) {
146
+ flags = sock->base.opts.keepalive_idle;
147
+ if (setsockopt(sock->fd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&flags, sizeof(flags)) < 0) {
148
+ goto fail;
149
+ }
150
+ }
151
+ #endif
152
+ #ifdef TCP_KEEPINTVL
153
+ if (sock->base.opts.keepalive_interval > 0) {
154
+ flags = sock->base.opts.keepalive_interval;
155
+ if (setsockopt(sock->fd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&flags, sizeof(flags)) < 0) {
156
+ goto fail;
157
+ }
158
+ }
159
+ #endif
160
+ #ifdef TCP_KEEPCNT
161
+ if (sock->base.opts.keepalive_count > 0) {
162
+ flags = sock->base.opts.keepalive_count;
163
+ if (setsockopt(sock->fd, IPPROTO_TCP, TCP_KEEPCNT, (void *)&flags, sizeof(flags)) < 0) {
164
+ goto fail;
165
+ }
166
+ }
167
+ #endif
168
+ }
169
+
170
+ if (set_nonblocking_fd(sock->fd) < 0) {
171
+ goto fail;
172
+ }
173
+
174
+ if (connect(sock->fd, ai->ai_addr, ai->ai_addrlen) < 0) {
175
+ if (errno != EINPROGRESS && errno != EAGAIN) {
176
+ goto fail;
177
+ }
178
+ }
179
+
180
+ if (trilogy_sock_wait_write((trilogy_sock_t *)sock) < 0) {
181
+ goto fail;
182
+ }
183
+
184
+ if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerr_len) < 0) {
185
+ goto fail;
186
+ }
187
+
188
+ if (sockerr != 0) {
189
+ // the socket failed to connect; since `getsockopt` doesn't set `errno`
190
+ // to a meaningful value, we must set it manually because clients always
191
+ // expect to find the error code there
192
+ errno = sockerr;
193
+ goto fail;
194
+ }
195
+
196
+ return TRILOGY_OK;
197
+
198
+ fail:
199
+ close(sock->fd);
200
+ sock->fd = -1;
201
+ return TRILOGY_SYSERR;
202
+ }
203
+
204
+ static int _cb_raw_connect(trilogy_sock_t *_sock)
205
+ {
206
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
207
+ const struct addrinfo *ai = sock->addr;
208
+
209
+ for (; ai; ai = ai->ai_next) {
210
+ int rc = raw_connect_internal(sock, ai);
211
+ if (rc == TRILOGY_OK)
212
+ return TRILOGY_OK;
213
+ else if (!ai->ai_next)
214
+ return rc;
215
+ }
216
+
217
+ return TRILOGY_ERR;
218
+ }
219
+
220
+ static char *strdupnullok(const char *str)
221
+ {
222
+ if (str == NULL) {
223
+ return NULL;
224
+ }
225
+ return strdup(str);
226
+ }
227
+
228
+ trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts)
229
+ {
230
+ struct trilogy_sock *sock = malloc(sizeof(struct trilogy_sock));
231
+
232
+ sock->base.connect_cb = _cb_raw_connect;
233
+ sock->base.read_cb = _cb_raw_read;
234
+ sock->base.write_cb = _cb_raw_write;
235
+ sock->base.wait_cb = _cb_wait;
236
+ sock->base.shutdown_cb = _cb_raw_shutdown;
237
+ sock->base.close_cb = _cb_raw_close;
238
+ sock->base.fd_cb = _cb_raw_fd;
239
+ sock->base.opts = *opts;
240
+
241
+ sock->base.opts.hostname = strdupnullok(opts->hostname);
242
+ sock->base.opts.path = strdupnullok(opts->path);
243
+ sock->base.opts.database = strdupnullok(opts->database);
244
+ sock->base.opts.username = strdupnullok(opts->username);
245
+
246
+ if (sock->base.opts.password) {
247
+ sock->base.opts.password = malloc(opts->password_len);
248
+ memcpy(sock->base.opts.password, opts->password, opts->password_len);
249
+ }
250
+
251
+ sock->base.opts.ssl_ca = strdupnullok(opts->ssl_ca);
252
+ sock->base.opts.ssl_capath = strdupnullok(opts->ssl_capath);
253
+ sock->base.opts.ssl_cert = strdupnullok(opts->ssl_cert);
254
+ sock->base.opts.ssl_cipher = strdupnullok(opts->ssl_cipher);
255
+ sock->base.opts.ssl_crl = strdupnullok(opts->ssl_crl);
256
+ sock->base.opts.ssl_crlpath = strdupnullok(opts->ssl_crlpath);
257
+ sock->base.opts.ssl_key = strdupnullok(opts->ssl_key);
258
+ sock->base.opts.tls_ciphersuites = strdupnullok(opts->tls_ciphersuites);
259
+
260
+ sock->fd = -1;
261
+ sock->addr = NULL;
262
+ sock->ssl = NULL;
263
+
264
+ return (trilogy_sock_t *)sock;
265
+ }
266
+
267
+ int trilogy_sock_resolve(trilogy_sock_t *_sock)
268
+ {
269
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
270
+
271
+ if (sock->base.opts.hostname != NULL) {
272
+ struct addrinfo hint = {.ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM};
273
+
274
+ char port[6];
275
+ snprintf(port, sizeof(port), "%hu", sock->base.opts.port);
276
+
277
+ if (getaddrinfo(sock->base.opts.hostname, port, &hint, &sock->addr) != 0) {
278
+ return TRILOGY_DNS_ERR;
279
+ }
280
+ } else if (sock->base.opts.path != NULL) {
281
+ struct sockaddr_un *sa;
282
+
283
+ if (strlen(sock->base.opts.path) + 1 > sizeof(sa->sun_path)) {
284
+ goto fail;
285
+ }
286
+
287
+ sa = calloc(1, sizeof(struct sockaddr_un));
288
+ sa->sun_family = AF_UNIX;
289
+ strcpy(sa->sun_path, sock->base.opts.path);
290
+
291
+ sock->addr = calloc(1, sizeof(struct addrinfo));
292
+ sock->addr->ai_family = PF_UNIX;
293
+ sock->addr->ai_socktype = SOCK_STREAM;
294
+ sock->addr->ai_addr = (struct sockaddr *)sa;
295
+ sock->addr->ai_addrlen = sizeof(struct sockaddr_un);
296
+ } else {
297
+ goto fail;
298
+ }
299
+
300
+ return TRILOGY_OK;
301
+
302
+ fail:
303
+ _cb_raw_close(_sock);
304
+ return TRILOGY_ERR;
305
+ }
306
+
307
+ static ssize_t ssl_io_return(struct trilogy_sock *sock, ssize_t ret)
308
+ {
309
+ if (ret < 0) {
310
+ int rc = SSL_get_error(sock->ssl, ret);
311
+ if (rc == SSL_ERROR_WANT_WRITE || rc == SSL_ERROR_WANT_READ) {
312
+ return (ssize_t)TRILOGY_AGAIN;
313
+ } else if (rc == SSL_ERROR_SYSCALL && errno != 0) {
314
+ return (ssize_t)TRILOGY_SYSERR;
315
+ }
316
+ return (ssize_t)TRILOGY_OPENSSL_ERR;
317
+ }
318
+ return ret;
319
+ }
320
+
321
+ static ssize_t _cb_ssl_read(trilogy_sock_t *_sock, void *buf, size_t nread)
322
+ {
323
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
324
+ ssize_t data_read = (ssize_t)SSL_read(sock->ssl, buf, (int)nread);
325
+ return ssl_io_return(sock, data_read);
326
+ }
327
+
328
+ static ssize_t _cb_ssl_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite)
329
+ {
330
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
331
+ ssize_t data_written = (ssize_t)SSL_write(sock->ssl, buf, (int)nwrite);
332
+ return ssl_io_return(sock, data_written);
333
+ }
334
+
335
+ static int _cb_ssl_shutdown(trilogy_sock_t *_sock)
336
+ {
337
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
338
+
339
+ // If we have an SSL socket, it's invalid here and
340
+ // we need to close it. The OpenSSL explicitly states
341
+ // not to call SSL_shutdown on a broken SSL socket.
342
+ SSL_free(sock->ssl);
343
+ // Reset the handlers since we tore down SSL, so we
344
+ // fall back to the regular methods for detecting
345
+ // we have a closed connection and for the cleanup.
346
+ sock->base.read_cb = _cb_raw_read;
347
+ sock->base.write_cb = _cb_raw_write;
348
+ sock->base.shutdown_cb = _cb_raw_shutdown;
349
+ sock->base.close_cb = _cb_raw_close;
350
+ sock->ssl = NULL;
351
+
352
+ return _cb_raw_shutdown(_sock);
353
+ }
354
+
355
+ static int _cb_ssl_close(trilogy_sock_t *_sock)
356
+ {
357
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
358
+ if (sock->ssl != NULL) {
359
+ SSL_shutdown(sock->ssl);
360
+ SSL_free(sock->ssl);
361
+ sock->ssl = NULL;
362
+ }
363
+ return _cb_raw_close(_sock);
364
+ }
365
+
366
+ #if OPENSSL_VERSION_NUMBER >= 0x1010000fL
367
+
368
+ static int trilogy_tls_version_map[] = {0, TLS1_VERSION, TLS1_1_VERSION, TLS1_2_VERSION
369
+ #ifdef TLS1_3_VERSION
370
+ ,
371
+ TLS1_3_VERSION
372
+ #else
373
+ ,
374
+ 0
375
+ #endif
376
+ };
377
+
378
+ int trilogy_set_min_proto_version(SSL_CTX *ctx, trilogy_tls_version_t version)
379
+ {
380
+ int ssl_ver = trilogy_tls_version_map[version];
381
+ if (ssl_ver == 0) {
382
+ ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_CTX_SET_SSL_VERSION, SSL_R_UNSUPPORTED_PROTOCOL, NULL, 0);
383
+ return 0;
384
+ }
385
+ return SSL_CTX_set_min_proto_version(ctx, ssl_ver);
386
+ }
387
+
388
+ int trilogy_set_max_proto_version(SSL_CTX *ctx, trilogy_tls_version_t version)
389
+ {
390
+ int ssl_ver = trilogy_tls_version_map[version];
391
+ if (ssl_ver == 0) {
392
+ ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_CTX_SET_SSL_VERSION, SSL_R_UNSUPPORTED_PROTOCOL, NULL, 0);
393
+ return 0;
394
+ }
395
+ return SSL_CTX_set_max_proto_version(ctx, ssl_ver);
396
+ }
397
+ #else
398
+
399
+ int trilogy_set_min_proto_version(SSL_CTX *ctx, trilogy_tls_version_t version)
400
+ {
401
+ long opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
402
+ switch (version) {
403
+ case TRILOGY_TLS_VERSION_13:
404
+ opts |= SSL_OP_NO_TLSv1_2;
405
+ case TRILOGY_TLS_VERSION_12:
406
+ opts |= SSL_OP_NO_TLSv1_1;
407
+ case TRILOGY_TLS_VERSION_11:
408
+ opts |= SSL_OP_NO_TLSv1;
409
+ default:
410
+ break;
411
+ }
412
+ // No need to handle 1.3 here since OpenSSL < 1.1.0 doesn't support that
413
+ // anyway
414
+ SSL_CTX_set_options(ctx, opts);
415
+ return 1;
416
+ }
417
+
418
+ int trilogy_set_max_proto_version(SSL_CTX *ctx, trilogy_tls_version_t version)
419
+ {
420
+ long opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
421
+ switch (version) {
422
+ case TRILOGY_TLS_VERSION_10:
423
+ opts |= SSL_OP_NO_TLSv1_1;
424
+ case TRILOGY_TLS_VERSION_11:
425
+ opts |= SSL_OP_NO_TLSv1_2;
426
+ default:
427
+ break;
428
+ }
429
+ SSL_CTX_set_options(ctx, opts);
430
+ return 1;
431
+ }
432
+ #endif
433
+
434
+ static SSL_CTX *trilogy_ssl_ctx(const trilogy_sockopt_t *opts)
435
+ {
436
+ SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv23_client_method());
437
+
438
+ // Now handle all the custom options that we're given.
439
+ if (opts->tls_min_version != TRILOGY_TLS_VERSION_UNDEF) {
440
+ if (!trilogy_set_min_proto_version(ssl_ctx, opts->tls_min_version)) {
441
+ goto fail;
442
+ }
443
+ } else {
444
+ if (!trilogy_set_min_proto_version(ssl_ctx, TRILOGY_TLS_VERSION_12)) {
445
+ goto fail;
446
+ }
447
+ }
448
+
449
+ if (opts->tls_max_version != TRILOGY_TLS_VERSION_UNDEF) {
450
+ if (!trilogy_set_max_proto_version(ssl_ctx, opts->tls_max_version)) {
451
+ goto fail;
452
+ }
453
+ }
454
+
455
+ if (opts->ssl_cipher) {
456
+ if (!SSL_CTX_set_cipher_list(ssl_ctx, opts->ssl_cipher)) {
457
+ goto fail;
458
+ }
459
+ } else {
460
+ // Use a secure cipher list, based on TLS 1.2 and with authenticated
461
+ // encryption.
462
+ if (!SSL_CTX_set_cipher_list(ssl_ctx, "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:"
463
+ "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:"
464
+ "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305")) {
465
+ goto fail;
466
+ }
467
+ }
468
+
469
+ #if OPENSSL_VERSION_NUMBER >= 0x1010100fL
470
+ if (opts->tls_ciphersuites) {
471
+ if (!SSL_CTX_set_ciphersuites(ssl_ctx, opts->tls_ciphersuites)) {
472
+ goto fail;
473
+ }
474
+ }
475
+ #endif
476
+
477
+ switch (opts->ssl_mode) {
478
+ case TRILOGY_SSL_DISABLED:
479
+ break;
480
+ case TRILOGY_SSL_VERIFY_IDENTITY:
481
+ case TRILOGY_SSL_VERIFY_CA:
482
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
483
+ break;
484
+ case TRILOGY_SSL_REQUIRED_NOVERIFY:
485
+ case TRILOGY_SSL_PREFERRED_NOVERIFY:
486
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
487
+ break;
488
+ }
489
+
490
+ if (opts->ssl_ca || opts->ssl_capath) {
491
+ if (!SSL_CTX_load_verify_locations(ssl_ctx, opts->ssl_ca, opts->ssl_capath)) {
492
+ goto fail;
493
+ }
494
+ } else {
495
+ // Use the default systems paths to verify the certificate
496
+ if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) {
497
+ goto fail;
498
+ }
499
+ }
500
+
501
+ if (opts->ssl_cert || opts->ssl_key) {
502
+ if (opts->ssl_key) {
503
+ if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, opts->ssl_key, SSL_FILETYPE_PEM)) {
504
+ goto fail;
505
+ }
506
+ }
507
+ if (opts->ssl_cert) {
508
+ if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, opts->ssl_cert)) {
509
+ goto fail;
510
+ }
511
+ }
512
+
513
+ if (!SSL_CTX_check_private_key(ssl_ctx)) {
514
+ goto fail;
515
+ }
516
+ }
517
+
518
+ if (opts->ssl_crl || opts->ssl_crlpath) {
519
+ X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
520
+
521
+ if (!X509_STORE_load_locations(store, opts->ssl_crl, opts->ssl_crlpath)) {
522
+ goto fail;
523
+ }
524
+
525
+ if (!X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL)) {
526
+ goto fail;
527
+ }
528
+ }
529
+ return ssl_ctx;
530
+
531
+ fail:
532
+ SSL_CTX_free(ssl_ctx);
533
+ return NULL;
534
+ }
535
+
536
+ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
537
+ {
538
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
539
+
540
+ SSL_CTX *ctx = trilogy_ssl_ctx(&sock->base.opts);
541
+
542
+ if (!ctx) {
543
+ return TRILOGY_OPENSSL_ERR;
544
+ }
545
+
546
+ sock->ssl = SSL_new(ctx);
547
+ SSL_CTX_free(ctx);
548
+
549
+ if (sock->base.opts.ssl_mode == TRILOGY_SSL_VERIFY_IDENTITY && sock->base.opts.hostname == NULL) {
550
+ // If hostname validation is requested and no hostname provided, treat it as an error.
551
+ #if OPENSSL_VERSION_NUMBER >= 0x1010000fL
552
+ ERR_put_error(ERR_LIB_SSL, SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
553
+ #else
554
+ ERR_put_error(ERR_LIB_SSL, SSL_F_SSL3_GET_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
555
+ #endif
556
+ goto fail;
557
+ }
558
+
559
+ // Set the SNI hostname for the connection
560
+ if (sock->base.opts.hostname != NULL) {
561
+ if (!SSL_set_tlsext_host_name(sock->ssl, sock->base.opts.hostname)) {
562
+ goto fail;
563
+ }
564
+ }
565
+
566
+ // Newer API available since 1.0.2, so we don't have to do manual work.
567
+ #if OPENSSL_VERSION_NUMBER >= 0x1000200fL
568
+ if (sock->base.opts.ssl_mode == TRILOGY_SSL_VERIFY_IDENTITY) {
569
+ X509_VERIFY_PARAM *param = SSL_get0_param(sock->ssl);
570
+ const char *hostname = sock->base.opts.hostname;
571
+ X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
572
+ if (!X509_VERIFY_PARAM_set1_host(param, hostname, strlen(hostname))) {
573
+ goto fail;
574
+ }
575
+ }
576
+ #endif
577
+
578
+ if (!SSL_set_fd(sock->ssl, sock->fd))
579
+ goto fail;
580
+
581
+ for (;;) {
582
+ int ret = SSL_connect(sock->ssl);
583
+ if (ret == 1) {
584
+ #if OPENSSL_VERSION_NUMBER < 0x1000200fL
585
+ if (sock->base.opts.ssl_mode == TRILOGY_SSL_VERIFY_IDENTITY) {
586
+ if (validate_hostname(sock->base.opts.hostname, SSL_get_peer_certificate(sock->ssl)) != MatchFound) {
587
+ // Fake the error message to be the same as it would be on 1.0.2 and newer.
588
+ ERR_put_error(ERR_LIB_SSL, SSL_F_SSL3_GET_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL,
589
+ 0);
590
+ goto fail;
591
+ }
592
+ }
593
+ #endif
594
+ break;
595
+ }
596
+
597
+ switch (SSL_get_error(sock->ssl, ret)) {
598
+ case SSL_ERROR_WANT_READ:
599
+ if (trilogy_sock_wait_read(_sock) < 0)
600
+ goto fail;
601
+ break;
602
+
603
+ case SSL_ERROR_WANT_WRITE:
604
+ if (trilogy_sock_wait_write(_sock) < 0)
605
+ goto fail;
606
+ break;
607
+
608
+ default:
609
+ goto fail;
610
+ }
611
+ }
612
+
613
+ sock->base.read_cb = _cb_ssl_read;
614
+ sock->base.write_cb = _cb_ssl_write;
615
+ sock->base.shutdown_cb = _cb_ssl_shutdown;
616
+ sock->base.close_cb = _cb_ssl_close;
617
+ return TRILOGY_OK;
618
+
619
+ fail:
620
+ SSL_free(sock->ssl);
621
+ sock->ssl = NULL;
622
+ return TRILOGY_OPENSSL_ERR;
623
+ }