trilogy 2.4.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,6 @@
1
1
  #include <netdb.h>
2
2
  #include <netinet/tcp.h>
3
+ #include <netinet/in.h>
3
4
  #include <poll.h>
4
5
  #include <sys/socket.h>
5
6
  #include <sys/types.h>
@@ -65,7 +66,11 @@ static ssize_t _cb_raw_read(trilogy_sock_t *_sock, void *buf, size_t nread)
65
66
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
66
67
  ssize_t data_read = read(sock->fd, buf, nread);
67
68
  if (data_read < 0) {
68
- return (ssize_t)TRILOGY_SYSERR;
69
+ if (errno == EINTR || errno == EAGAIN) {
70
+ return (ssize_t)TRILOGY_AGAIN;
71
+ } else {
72
+ return (ssize_t)TRILOGY_SYSERR;
73
+ }
69
74
  }
70
75
  return data_read;
71
76
  }
@@ -75,6 +80,14 @@ static ssize_t _cb_raw_write(trilogy_sock_t *_sock, const void *buf, size_t nwri
75
80
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
76
81
  ssize_t data_written = write(sock->fd, buf, nwrite);
77
82
  if (data_written < 0) {
83
+ if (errno == EINTR || errno == EAGAIN) {
84
+ return (ssize_t)TRILOGY_AGAIN;
85
+ }
86
+
87
+ if (errno == EPIPE) {
88
+ return (ssize_t)TRILOGY_CLOSED_CONNECTION;
89
+ }
90
+
78
91
  return (ssize_t)TRILOGY_SYSERR;
79
92
  }
80
93
  return data_written;
@@ -109,7 +122,53 @@ static int _cb_raw_close(trilogy_sock_t *_sock)
109
122
  return rc;
110
123
  }
111
124
 
112
- static int _cb_raw_shutdown(trilogy_sock_t *_sock) { return shutdown(trilogy_sock_fd(_sock), SHUT_RDWR); }
125
+ static int _cb_shutdown_connect(trilogy_sock_t *_sock) {
126
+ (void)_sock;
127
+ return TRILOGY_CLOSED_CONNECTION;
128
+ }
129
+ static ssize_t _cb_shutdown_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite) {
130
+ (void)_sock;
131
+ (void)buf;
132
+ (void)nwrite;
133
+ return TRILOGY_CLOSED_CONNECTION;
134
+ }
135
+ static ssize_t _cb_shutdown_read(trilogy_sock_t *_sock, void *buf, size_t nread) {
136
+ (void)_sock;
137
+ (void)buf;
138
+ (void)nread;
139
+ return TRILOGY_CLOSED_CONNECTION;
140
+ }
141
+ static int _cb_shutdown_wait(trilogy_sock_t *_sock, trilogy_wait_t wait) {
142
+ (void)_sock;
143
+ (void)wait;
144
+ return TRILOGY_OK;
145
+ }
146
+ static int _cb_shutdown_shutdown(trilogy_sock_t *_sock) {
147
+ (void)_sock;
148
+ return TRILOGY_OK;
149
+ }
150
+
151
+ // Shutdown will close the underlying socket fd and replace all I/O operations with stubs which perform no action.
152
+ static int _cb_raw_shutdown(trilogy_sock_t *_sock) {
153
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
154
+
155
+ // Replace all operations with stubs which return immediately
156
+ sock->base.connect_cb = _cb_shutdown_connect;
157
+ sock->base.read_cb = _cb_shutdown_read;
158
+ sock->base.write_cb = _cb_shutdown_write;
159
+ sock->base.wait_cb = _cb_shutdown_wait;
160
+ sock->base.shutdown_cb = _cb_shutdown_shutdown;
161
+
162
+ // These "raw" callbacks won't attempt further operations on the socket and work correctly with fd set to -1
163
+ sock->base.close_cb = _cb_raw_close;
164
+ sock->base.fd_cb = _cb_raw_fd;
165
+
166
+ if (sock->fd != -1)
167
+ close(sock->fd);
168
+ sock->fd = -1;
169
+
170
+ return TRILOGY_OK;
171
+ }
113
172
 
114
173
  static int set_nonblocking_fd(int sock)
115
174
  {
@@ -136,6 +195,14 @@ static int raw_connect_internal(struct trilogy_sock *sock, const struct addrinfo
136
195
  return TRILOGY_SYSERR;
137
196
  }
138
197
 
198
+ #ifdef TCP_NODELAY
199
+ if (sock->addr->ai_family != PF_UNIX) {
200
+ int flags = 1;
201
+ if (setsockopt(sock->fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) < 0) {
202
+ goto fail;
203
+ }
204
+ }
205
+ #endif
139
206
  if (sock->base.opts.keepalive_enabled) {
140
207
  int flags = 1;
141
208
  if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0) {
@@ -306,12 +373,18 @@ fail:
306
373
 
307
374
  static ssize_t ssl_io_return(struct trilogy_sock *sock, ssize_t ret)
308
375
  {
309
- if (ret < 0) {
376
+ if (ret <= 0) {
310
377
  int rc = SSL_get_error(sock->ssl, (int)ret);
311
378
  if (rc == SSL_ERROR_WANT_WRITE || rc == SSL_ERROR_WANT_READ) {
312
379
  return (ssize_t)TRILOGY_AGAIN;
313
- } else if (rc == SSL_ERROR_SYSCALL && errno != 0) {
314
- return (ssize_t)TRILOGY_SYSERR;
380
+ } else if (rc == SSL_ERROR_SYSCALL && !ERR_peek_error()) {
381
+ if (errno == 0) {
382
+ // On OpenSSL <= 1.1.1, SSL_ERROR_SYSCALL with an errno value
383
+ // of 0 indicates unexpected EOF from the peer.
384
+ return (ssize_t)TRILOGY_CLOSED_CONNECTION;
385
+ } else {
386
+ return (ssize_t)TRILOGY_SYSERR;
387
+ }
315
388
  }
316
389
  return (ssize_t)TRILOGY_OPENSSL_ERR;
317
390
  }
@@ -321,6 +394,11 @@ static ssize_t ssl_io_return(struct trilogy_sock *sock, ssize_t ret)
321
394
  static ssize_t _cb_ssl_read(trilogy_sock_t *_sock, void *buf, size_t nread)
322
395
  {
323
396
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
397
+
398
+ // This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
399
+ // in the queue.
400
+ ERR_clear_error();
401
+
324
402
  ssize_t data_read = (ssize_t)SSL_read(sock->ssl, buf, (int)nread);
325
403
  return ssl_io_return(sock, data_read);
326
404
  }
@@ -328,6 +406,11 @@ static ssize_t _cb_ssl_read(trilogy_sock_t *_sock, void *buf, size_t nread)
328
406
  static ssize_t _cb_ssl_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite)
329
407
  {
330
408
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
409
+
410
+ // This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
411
+ // in the queue.
412
+ ERR_clear_error();
413
+
331
414
  ssize_t data_written = (ssize_t)SSL_write(sock->ssl, buf, (int)nwrite);
332
415
  return ssl_io_return(sock, data_written);
333
416
  }
@@ -340,15 +423,9 @@ static int _cb_ssl_shutdown(trilogy_sock_t *_sock)
340
423
  // we need to close it. The OpenSSL explicitly states
341
424
  // not to call SSL_shutdown on a broken SSL socket.
342
425
  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
426
  sock->ssl = NULL;
351
427
 
428
+ // This will rewrite the handlers
352
429
  return _cb_raw_shutdown(_sock);
353
430
  }
354
431
 
@@ -356,7 +433,12 @@ static int _cb_ssl_close(trilogy_sock_t *_sock)
356
433
  {
357
434
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
358
435
  if (sock->ssl != NULL) {
359
- SSL_shutdown(sock->ssl);
436
+ if (SSL_in_init(sock->ssl) == 0) {
437
+ (void)SSL_shutdown(sock->ssl);
438
+ // SSL_shutdown might return WANT_WRITE or WANT_READ. Ideally we would retry but we don't want to block.
439
+ // It may also push an error onto the OpenSSL error queue, so clear that.
440
+ ERR_clear_error();
441
+ }
360
442
  SSL_free(sock->ssl);
361
443
  sock->ssl = NULL;
362
444
  }
@@ -537,6 +619,10 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
537
619
  {
538
620
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
539
621
 
622
+ // This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
623
+ // in the queue.
624
+ ERR_clear_error();
625
+
540
626
  SSL_CTX *ctx = trilogy_ssl_ctx(&sock->base.opts);
541
627
 
542
628
  if (!ctx) {
@@ -548,7 +634,7 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
548
634
 
549
635
  if (sock->base.opts.ssl_mode == TRILOGY_SSL_VERIFY_IDENTITY && sock->base.opts.hostname == NULL) {
550
636
  // If hostname validation is requested and no hostname provided, treat it as an error.
551
- #if OPENSSL_VERSION_NUMBER >= 0x1010000fL
637
+ #ifdef SSL_F_TLS_PROCESS_SERVER_CERTIFICATE
552
638
  ERR_put_error(ERR_LIB_SSL, SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
553
639
  #else
554
640
  ERR_put_error(ERR_LIB_SSL, SSL_F_SSL3_GET_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
@@ -579,6 +665,10 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
579
665
  goto fail;
580
666
 
581
667
  for (;;) {
668
+ // This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
669
+ // in the queue.
670
+ ERR_clear_error();
671
+
582
672
  int ret = SSL_connect(sock->ssl);
583
673
  if (ret == 1) {
584
674
  #if OPENSSL_VERSION_NUMBER < 0x1000200fL
@@ -621,28 +711,3 @@ fail:
621
711
  sock->ssl = NULL;
622
712
  return TRILOGY_OPENSSL_ERR;
623
713
  }
624
-
625
- int trilogy_sock_discard(trilogy_sock_t *_sock)
626
- {
627
- struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
628
-
629
- if (sock->fd < 0) {
630
- return TRILOGY_OK;
631
- }
632
-
633
- int null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
634
- if (null_fd < 0) {
635
- return TRILOGY_SYSERR;
636
- }
637
-
638
- if (dup2(null_fd, sock->fd) < 0) {
639
- close(null_fd);
640
- return TRILOGY_SYSERR;
641
- }
642
-
643
- if (close(null_fd) < 0) {
644
- return TRILOGY_SYSERR;
645
- }
646
-
647
- return TRILOGY_OK;
648
- }
@@ -0,0 +1,97 @@
1
+ class Trilogy
2
+ module Encoding
3
+ RUBY_ENCODINGS = {
4
+ "big5" => "Big5",
5
+ "dec8" => nil,
6
+ "cp850" => "CP850",
7
+ "hp8" => nil,
8
+ "koi8r" => "KOI8-R",
9
+ "latin1" => "ISO-8859-1",
10
+ "latin2" => "ISO-8859-2",
11
+ "swe7" => nil,
12
+ "ascii" => "US-ASCII",
13
+ "ujis" => "eucJP-ms",
14
+ "sjis" => "Shift_JIS",
15
+ "hebrew" => "ISO-8859-8",
16
+ "tis620" => "TIS-620",
17
+ "euckr" => "EUC-KR",
18
+ "koi8u" => "KOI8-R",
19
+ "gb2312" => "GB2312",
20
+ "greek" => "ISO-8859-7",
21
+ "cp1250" => "Windows-1250",
22
+ "gbk" => "GBK",
23
+ "latin5" => "ISO-8859-9",
24
+ "armscii8" => nil,
25
+ "utf8" => "UTF-8",
26
+ "ucs2" => "UTF-16BE",
27
+ "cp866" => "IBM866",
28
+ "keybcs2" => nil,
29
+ "macce" => "macCentEuro",
30
+ "macroman" => "macRoman",
31
+ "cp852" => "CP852",
32
+ "latin7" => "ISO-8859-13",
33
+ "utf8mb4" => "UTF-8",
34
+ "cp1251" => "Windows-1251",
35
+ "utf16" => "UTF-16",
36
+ "cp1256" => "Windows-1256",
37
+ "cp1257" => "Windows-1257",
38
+ "utf32" => "UTF-32",
39
+ "binary" => "ASCII-8BIT",
40
+ "geostd8" => nil,
41
+ "cp932" => "Windows-31J",
42
+ "eucjpms" => "eucJP-ms",
43
+ "utf16le" => "UTF-16LE",
44
+ "gb18030" => "GB18030",
45
+ }.freeze
46
+
47
+ CHARSETS = {
48
+ "big5" => CHARSET_BIG5_CHINESE_CI,
49
+ "cp850" => CHARSET_CP850_GENERAL_CI,
50
+ "koi8r" => CHARSET_KOI8R_GENERAL_CI,
51
+ "latin1" => CHARSET_LATIN1_GENERAL_CI,
52
+ "latin2" => CHARSET_LATIN2_GENERAL_CI,
53
+ "ascii" => CHARSET_ASCII_GENERAL_CI,
54
+ "ujis" => CHARSET_UJIS_JAPANESE_CI,
55
+ "sjis" => CHARSET_SJIS_JAPANESE_CI,
56
+ "hebrew" => CHARSET_HEBREW_GENERAL_CI,
57
+ "tis620" => CHARSET_TIS620_THAI_CI,
58
+ "euckr" => CHARSET_EUCKR_KOREAN_CI,
59
+ "koi8u" => CHARSET_KOI8U_GENERAL_CI,
60
+ "gb2312" => CHARSET_GB2312_CHINESE_CI,
61
+ "greek" => CHARSET_GREEK_GENERAL_CI,
62
+ "cp1250" => CHARSET_CP1250_GENERAL_CI,
63
+ "gbk" => CHARSET_GBK_CHINESE_CI,
64
+ "latin5" => CHARSET_LATIN5_TURKISH_CI,
65
+ "utf8" => CHARSET_UTF8_GENERAL_CI,
66
+ "ucs2" => CHARSET_UCS2_GENERAL_CI,
67
+ "cp866" => CHARSET_CP866_GENERAL_CI,
68
+ "cp932" => CHARSET_CP932_JAPANESE_CI,
69
+ "eucjpms" => CHARSET_EUCJPMS_JAPANESE_CI,
70
+ "utf16le" => CHARSET_UTF16_GENERAL_CI,
71
+ "gb18030" => CHARSET_GB18030_CHINESE_CI,
72
+ "macce" => CHARSET_MACCE_GENERAL_CI,
73
+ "macroman" => CHARSET_MACROMAN_GENERAL_CI,
74
+ "cp852" => CHARSET_CP852_GENERAL_CI,
75
+ "latin7" => CHARSET_LATIN7_GENERAL_CI,
76
+ "utf8mb4" => CHARSET_UTF8MB4_GENERAL_CI,
77
+ "cp1251" => CHARSET_CP1251_GENERAL_CI,
78
+ "utf16" => CHARSET_UTF16_GENERAL_CI,
79
+ "cp1256" => CHARSET_CP1256_GENERAL_CI,
80
+ "cp1257" => CHARSET_CP1257_GENERAL_CI,
81
+ "utf32" => CHARSET_UTF32_GENERAL_CI,
82
+ "binary" => CHARSET_BINARY,
83
+ }.freeze
84
+
85
+ def self.find(mysql_encoding)
86
+ unless rb_encoding = RUBY_ENCODINGS[mysql_encoding]
87
+ raise ArgumentError, "Unknown or unsupported encoding: #{mysql_encoding}"
88
+ end
89
+
90
+ ::Encoding.find(rb_encoding)
91
+ end
92
+
93
+ def self.charset(mysql_encoding)
94
+ CHARSETS[mysql_encoding]
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,124 @@
1
+ class Trilogy
2
+ # Trilogy::Error is the base error type. All errors raised by Trilogy
3
+ # should be descendants of Trilogy::Error
4
+ module Error
5
+ attr_reader :error_code
6
+ end
7
+
8
+ # Trilogy::ConnectionError is the base error type for all potentially transient
9
+ # network errors.
10
+ module ConnectionError
11
+ include Error
12
+ end
13
+
14
+ # Trilogy may raise various syscall errors, which we treat as Trilogy::Errors.
15
+ class SyscallError
16
+ ERRORS = {}
17
+
18
+ Errno.constants
19
+ .map { |c| Errno.const_get(c) }.uniq
20
+ .select { |c| c.is_a?(Class) && c < SystemCallError }
21
+ .each do |c|
22
+ errno_name = c.to_s.split('::').last
23
+ ERRORS[c::Errno] = const_set(errno_name, Class.new(c) { include Trilogy::ConnectionError })
24
+ end
25
+
26
+ ERRORS.freeze
27
+
28
+ class << self
29
+ def from_errno(errno, message)
30
+ ERRORS[errno].new(message)
31
+ end
32
+ end
33
+ end
34
+
35
+ class BaseError < StandardError
36
+ include Error
37
+
38
+ def initialize(error_message = nil, error_code = nil)
39
+ message = error_code ? "#{error_code}: #{error_message}" : error_message
40
+ super(message)
41
+ @error_code = error_code
42
+ end
43
+ end
44
+
45
+ class BaseConnectionError < BaseError
46
+ include ConnectionError
47
+ end
48
+
49
+ # Trilogy::ClientError is the base error type for invalid queries or parameters
50
+ # that shouldn't be retried.
51
+ class ClientError < BaseError
52
+ include Error
53
+ end
54
+
55
+ class QueryError < ClientError
56
+ end
57
+
58
+ class CastError < ClientError
59
+ end
60
+
61
+ class TimeoutError < Errno::ETIMEDOUT
62
+ include ConnectionError
63
+
64
+ def initialize(error_message = nil, error_code = nil)
65
+ super
66
+ @error_code = error_code
67
+ end
68
+ end
69
+
70
+ class ConnectionRefusedError < Errno::ECONNREFUSED
71
+ include ConnectionError
72
+ end
73
+
74
+ class ConnectionResetError < Errno::ECONNRESET
75
+ include ConnectionError
76
+ end
77
+
78
+ # DatabaseError was replaced by ProtocolError, but we'll keep it around as an
79
+ # ancestor of ProtocolError for compatibility reasons (e.g. so `rescue DatabaseError`
80
+ # still works. We can remove this class in the next major release.
81
+ module DatabaseError
82
+ end
83
+
84
+ class ProtocolError < BaseError
85
+ include DatabaseError
86
+
87
+ ERROR_CODES = {
88
+ 1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT
89
+ 1044 => BaseConnectionError, # ER_DBACCESS_DENIED_ERROR
90
+ 1045 => BaseConnectionError, # ER_ACCESS_DENIED_ERROR
91
+ 1064 => QueryError, # ER_PARSE_ERROR
92
+ 1152 => BaseConnectionError, # ER_ABORTING_CONNECTION
93
+ 1153 => BaseConnectionError, # ER_NET_PACKET_TOO_LARGE
94
+ 1154 => BaseConnectionError, # ER_NET_READ_ERROR_FROM_PIPE
95
+ 1155 => BaseConnectionError, # ER_NET_FCNTL_ERROR
96
+ 1156 => BaseConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER
97
+ 1157 => BaseConnectionError, # ER_NET_UNCOMPRESS_ERROR
98
+ 1158 => BaseConnectionError, # ER_NET_READ_ERROR
99
+ 1159 => BaseConnectionError, # ER_NET_READ_INTERRUPTED
100
+ 1160 => BaseConnectionError, # ER_NET_ERROR_ON_WRITE
101
+ 1161 => BaseConnectionError, # ER_NET_WRITE_INTERRUPTED
102
+ 1927 => BaseConnectionError, # ER_CONNECTION_KILLED
103
+ }
104
+ class << self
105
+ def from_code(message, code)
106
+ ERROR_CODES.fetch(code, self).new(message, code)
107
+ end
108
+ end
109
+ end
110
+
111
+ class SSLError < BaseError
112
+ include ConnectionError
113
+ end
114
+
115
+ # Raised on attempt to use connection which was explicitly closed by the user
116
+ class ConnectionClosed < IOError
117
+ include ConnectionError
118
+ end
119
+
120
+ # Occurrs when a socket read or write returns EOF or when an operation is
121
+ # attempted on a socket which previously encountered an error.
122
+ class EOFError < BaseConnectionError
123
+ end
124
+ end
@@ -0,0 +1,33 @@
1
+ class Trilogy
2
+ class Result
3
+ attr_reader :fields, :rows, :query_time, :affected_rows, :last_insert_id
4
+
5
+ def count
6
+ rows.count
7
+ end
8
+
9
+ def each_hash
10
+ return enum_for(:each_hash) unless block_given?
11
+
12
+ rows.each do |row|
13
+ this_row = {}
14
+
15
+ idx = 0
16
+ row.each do |col|
17
+ this_row[fields[idx]] = col
18
+ idx += 1
19
+ end
20
+
21
+ yield this_row
22
+ end
23
+
24
+ self
25
+ end
26
+
27
+ def each(&bk)
28
+ rows.each(&bk)
29
+ end
30
+
31
+ include Enumerable
32
+ end
33
+ end
@@ -1,3 +1,3 @@
1
1
  class Trilogy
2
- VERSION = "2.4.1"
2
+ VERSION = "2.6.0"
3
3
  end