trilogy 2.6.1 → 2.9.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d015527cefe1e66b73b4db1d31b98e3db6ab616edb1fb2da947054a55818f338
4
- data.tar.gz: 315bdd0e7dabf0f37ed69f5cd0884a702a7502443b1e26e5b01bec90b122e11d
3
+ metadata.gz: 998b06e77dcb78b2f30307834490a7c9c7d61422dea586f646cead7b74467058
4
+ data.tar.gz: e5bf1ecf9e8b2b675db0d3c3ee7531db4ab022aa725c4ed0c16db088884c6075
5
5
  SHA512:
6
- metadata.gz: b6e50321b8493b6ea8137d534e0fa8eedc5b7388093fee5e910dbe8c6bd81c0e0dc9c8d9c448895096fb8bd0244a0ee65b19b5f580c3b9137c76c9af221f0ab2
7
- data.tar.gz: 7d8b81caf8faaadc4ef548ffddab7a73f9b8d4d6ede5a0f9078c6ea65cd195f83bdcacdf5a3d7b71d76503161a689d8ac66b058a4c605a915f9a9dcf4aa56cdd
6
+ metadata.gz: bd2acff1c38bbed9bea64f3fbb9ad474e4d42198dccbcdca0aa1b8ba3b52e35711f1c9e2e1a6859c15a71c38ed08ba06b5adcb5fbb5054b1b4264fd51ae0933f
7
+ data.tar.gz: e032a04cfb901d48a4e1e711cd576c380562f2b7f871724d06568dff8bd3b599fd3ea86360b3bcbe2f8f9471f986d6b1cc8f8d8e69309877778d1ce442dd65f3
@@ -17,13 +17,13 @@
17
17
 
18
18
  VALUE Trilogy_CastError;
19
19
  static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
20
- Trilogy_ConnectionClosedError, Trilogy_ConnectionRefusedError, Trilogy_ConnectionResetError,
21
- Trilogy_TimeoutError, Trilogy_SyscallError, Trilogy_Result, Trilogy_EOFError;
20
+ Trilogy_ConnectionClosedError,
21
+ Trilogy_TimeoutError, Trilogy_SyscallError, Trilogy_Result, Trilogy_EOFError, Trilogy_AuthPluginError;
22
22
 
23
23
  static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
24
24
  id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
25
25
  id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
26
- id_database, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
26
+ id_database, id_enable_cleartext_plugin, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
27
27
  id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_multi_result,
28
28
  id_from_code, id_from_errno, id_connection_options, id_max_allowed_packet;
29
29
 
@@ -88,12 +88,8 @@ static struct trilogy_ctx *get_open_ctx(VALUE obj)
88
88
  NORETURN(static void trilogy_syserr_fail_str(int, VALUE));
89
89
  static void trilogy_syserr_fail_str(int e, VALUE msg)
90
90
  {
91
- if (e == ECONNREFUSED) {
92
- rb_raise(Trilogy_ConnectionRefusedError, "%" PRIsVALUE, msg);
93
- } else if (e == ECONNRESET) {
94
- rb_raise(Trilogy_ConnectionResetError, "%" PRIsVALUE, msg);
95
- } else if (e == EPIPE) {
96
- // Backwards compatibility: This error class makes no sense, but matches legacy behavior
91
+ if (e == EPIPE) {
92
+ // Backwards compatibility: This error message is a bit odd, but includes "TRILOGY_CLOSED_CONNECTION" to match legacy string matching
97
93
  rb_raise(Trilogy_EOFError, "%" PRIsVALUE ": TRILOGY_CLOSED_CONNECTION: EPIPE", msg);
98
94
  } else {
99
95
  VALUE exc = rb_funcall(Trilogy_SyscallError, id_from_errno, 2, INT2NUM(e), msg);
@@ -137,7 +133,8 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
137
133
  rb_raise(Trilogy_TimeoutError, "%" PRIsVALUE, rbmsg);
138
134
 
139
135
  case TRILOGY_ERR: {
140
- VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
136
+ VALUE conn_message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
137
+ VALUE message = rb_sprintf("%" PRIsVALUE " (%" PRIsVALUE ")", conn_message, rbmsg);
141
138
  VALUE exc = rb_funcall(Trilogy_ProtocolError, id_from_code, 2, message, INT2NUM(ctx->conn.error_code));
142
139
  rb_exc_raise(exc);
143
140
  }
@@ -160,6 +157,14 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
160
157
  rb_raise(Trilogy_EOFError, "%" PRIsVALUE ": TRILOGY_CLOSED_CONNECTION", rbmsg);
161
158
  }
162
159
 
160
+ case TRILOGY_AUTH_PLUGIN_ERROR: {
161
+ rb_raise(Trilogy_AuthPluginError, "%" PRIsVALUE ": TRILOGY_AUTH_PLUGIN_ERROR", rbmsg);
162
+ }
163
+
164
+ case TRILOGY_UNSUPPORTED: {
165
+ rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE ": TRILOGY_UNSUPPORTED", rbmsg);
166
+ }
167
+
163
168
  default:
164
169
  rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
165
170
  }
@@ -243,7 +248,18 @@ static int _cb_ruby_wait(trilogy_sock_t *sock, trilogy_wait_t wait)
243
248
  wait_flag = RB_WAITFD_OUT;
244
249
  break;
245
250
 
251
+ case TRILOGY_WAIT_CONNECT:
252
+ // wait for connection to be writable
253
+ timeout = &sock->opts.connect_timeout;
254
+ if (timeout->tv_sec == 0 && timeout->tv_usec == 0) {
255
+ // We used to use the write timeout for this, so if a connect timeout isn't configured, default to that.
256
+ timeout = &sock->opts.write_timeout;
257
+ }
258
+ wait_flag = RB_WAITFD_OUT;
259
+ break;
260
+
246
261
  case TRILOGY_WAIT_HANDSHAKE:
262
+ // wait for handshake packet on initial connection
247
263
  timeout = &sock->opts.connect_timeout;
248
264
  wait_flag = RB_WAITFD_IN;
249
265
  break;
@@ -412,7 +428,12 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
412
428
  }
413
429
 
414
430
  if (rc != TRILOGY_AGAIN) {
415
- handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
431
+ if (rc == TRILOGY_UNSUPPORTED) {
432
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv: caching_sha2_password requires either TCP with TLS or a unix socket");
433
+ }
434
+ else {
435
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
436
+ }
416
437
  }
417
438
 
418
439
  rc = trilogy_sock_wait_read(ctx->conn.socket);
@@ -515,6 +536,10 @@ static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE
515
536
  connopt.flags |= TRILOGY_CAPABILITIES_CONNECT_WITH_DB;
516
537
  }
517
538
 
539
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_enable_cleartext_plugin)))) {
540
+ connopt.enable_cleartext_plugin = true;
541
+ }
542
+
518
543
  if (RTEST(rb_hash_aref(opts, ID2SYM(id_found_rows)))) {
519
544
  connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
520
545
  }
@@ -1018,6 +1043,17 @@ static VALUE rb_trilogy_closed(VALUE self)
1018
1043
  }
1019
1044
  }
1020
1045
 
1046
+ static VALUE rb_trilogy_check(VALUE self)
1047
+ {
1048
+ struct trilogy_ctx *ctx = get_open_ctx(self);
1049
+
1050
+ int rc = trilogy_sock_check(ctx->conn.socket);
1051
+ if (rc != TRILOGY_OK && rc != TRILOGY_AGAIN) {
1052
+ handle_trilogy_error(ctx, rc, "trilogy_sock_check");
1053
+ }
1054
+ return Qtrue;
1055
+ }
1056
+
1021
1057
  static VALUE rb_trilogy_discard(VALUE self)
1022
1058
  {
1023
1059
  struct trilogy_ctx *ctx = get_ctx(self);
@@ -1096,8 +1132,12 @@ static VALUE rb_trilogy_server_status(VALUE self) { return LONG2FIX(get_open_ctx
1096
1132
 
1097
1133
  static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_open_ctx(self)->server_version); }
1098
1134
 
1099
- RUBY_FUNC_EXPORTED void Init_cext()
1135
+ RUBY_FUNC_EXPORTED void Init_cext(void)
1100
1136
  {
1137
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
1138
+ rb_ext_ractor_safe(true);
1139
+ #endif
1140
+
1101
1141
  VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
1102
1142
  rb_define_alloc_func(Trilogy, allocate_trilogy);
1103
1143
 
@@ -1109,6 +1149,7 @@ RUBY_FUNC_EXPORTED void Init_cext()
1109
1149
  rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
1110
1150
  rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
1111
1151
  rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
1152
+ rb_define_method(Trilogy, "check", rb_trilogy_check, 0);
1112
1153
  rb_define_method(Trilogy, "discard!", rb_trilogy_discard, 0);
1113
1154
  rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
1114
1155
  rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);
@@ -1156,12 +1197,6 @@ RUBY_FUNC_EXPORTED void Init_cext()
1156
1197
  Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
1157
1198
  rb_global_variable(&Trilogy_TimeoutError);
1158
1199
 
1159
- Trilogy_ConnectionRefusedError = rb_const_get(Trilogy, rb_intern("ConnectionRefusedError"));
1160
- rb_global_variable(&Trilogy_ConnectionRefusedError);
1161
-
1162
- Trilogy_ConnectionResetError = rb_const_get(Trilogy, rb_intern("ConnectionResetError"));
1163
- rb_global_variable(&Trilogy_ConnectionResetError);
1164
-
1165
1200
  Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
1166
1201
  rb_global_variable(&Trilogy_BaseConnectionError);
1167
1202
 
@@ -1180,6 +1215,9 @@ RUBY_FUNC_EXPORTED void Init_cext()
1180
1215
  Trilogy_EOFError = rb_const_get(Trilogy, rb_intern("EOFError"));
1181
1216
  rb_global_variable(&Trilogy_EOFError);
1182
1217
 
1218
+ rb_global_variable(&Trilogy_AuthPluginError);
1219
+ Trilogy_AuthPluginError = rb_const_get(Trilogy, rb_intern("AuthPluginError"));
1220
+
1183
1221
  id_socket = rb_intern("socket");
1184
1222
  id_host = rb_intern("host");
1185
1223
  id_port = rb_intern("port");
@@ -1195,6 +1233,7 @@ RUBY_FUNC_EXPORTED void Init_cext()
1195
1233
  id_keepalive_count = rb_intern("keepalive_count");
1196
1234
  id_keepalive_interval = rb_intern("keepalive_interval");
1197
1235
  id_database = rb_intern("database");
1236
+ id_enable_cleartext_plugin = rb_intern("enable_cleartext_plugin");
1198
1237
  id_ssl_ca = rb_intern("ssl_ca");
1199
1238
  id_ssl_capath = rb_intern("ssl_capath");
1200
1239
  id_ssl_cert = rb_intern("ssl_cert");
@@ -24,7 +24,8 @@
24
24
  XX(TRILOGY_AUTH_SWITCH, -19) \
25
25
  XX(TRILOGY_MAX_PACKET_EXCEEDED, -20) \
26
26
  XX(TRILOGY_UNKNOWN_TYPE, -21) \
27
- XX(TRILOGY_TIMEOUT, -22)
27
+ XX(TRILOGY_TIMEOUT, -22) \
28
+ XX(TRILOGY_AUTH_PLUGIN_ERROR, -23)
28
29
 
29
30
  enum {
30
31
  #define XX(name, code) name = code,
@@ -289,6 +289,7 @@ typedef enum {
289
289
  XX(TRILOGY_TYPE_YEAR, 0x0d) \
290
290
  XX(TRILOGY_TYPE_VARCHAR, 0x0f) \
291
291
  XX(TRILOGY_TYPE_BIT, 0x10) \
292
+ XX(TRILOGY_TYPE_VECTOR, 0xf2) \
292
293
  XX(TRILOGY_TYPE_JSON, 0xf5) \
293
294
  XX(TRILOGY_TYPE_NEWDECIMAL, 0xf6) \
294
295
  XX(TRILOGY_TYPE_ENUM, 0xf7) \
@@ -367,6 +368,7 @@ typedef enum {
367
368
  // Typical response packet types
368
369
  typedef enum {
369
370
  TRILOGY_PACKET_OK = 0x0,
371
+ TRILOGY_PACKET_AUTH_MORE_DATA = 0x01,
370
372
  TRILOGY_PACKET_EOF = 0xfe,
371
373
  TRILOGY_PACKET_ERR = 0xff,
372
374
  TRILOGY_PACKET_UNKNOWN
@@ -442,14 +444,16 @@ int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, cons
442
444
  * pass_len - The length of password in bytes.
443
445
  * auth_plugin - Plugin authentication mechanism that the server requested.
444
446
  * scramble - The scramble value received from the server.
447
+ * enable_cleartext_plugin - Send cleartext password if requested by server.
445
448
  *
446
449
  * Return values:
447
450
  * TRILOGY_OK - The packet was successfully built and written to the
448
451
  * builder's internal buffer.
449
452
  * TRILOGY_SYSERR - A system error occurred, check errno.
453
+ * TRILOGY_AUTH_PLUGIN_ERROR - The server requested auth plugin is not supported.
450
454
  */
451
455
  int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const char *pass, size_t pass_len,
452
- const char *auth_plugin, const char *scramble);
456
+ const char *auth_plugin, const char *scramble, const bool enable_cleartext_plugin);
453
457
 
454
458
  /* trilogy_build_change_db_packet - Build a change database command packet. This
455
459
  * command will change the default database for the connection.
@@ -1035,4 +1039,6 @@ int trilogy_parse_stmt_ok_packet(const uint8_t *buff, size_t len, trilogy_stmt_o
1035
1039
  int trilogy_parse_stmt_row_packet(const uint8_t *buff, size_t len, trilogy_column_packet_t *columns,
1036
1040
  uint64_t column_count, trilogy_binary_value_t *out_values);
1037
1041
 
1042
+ int trilogy_build_auth_clear_password(trilogy_builder_t *builder, const char *pass, size_t pass_len);
1043
+
1038
1044
  #endif
@@ -13,6 +13,7 @@ typedef enum {
13
13
  TRILOGY_WAIT_READ = 0,
14
14
  TRILOGY_WAIT_WRITE = 1,
15
15
  TRILOGY_WAIT_HANDSHAKE = 2,
16
+ TRILOGY_WAIT_CONNECT = 3,
16
17
  } trilogy_wait_t;
17
18
 
18
19
  // We use the most strict mode as value 1 so if anyone ever
@@ -66,6 +67,8 @@ typedef struct {
66
67
  uint16_t keepalive_count;
67
68
  uint16_t keepalive_interval;
68
69
 
70
+ bool enable_cleartext_plugin;
71
+
69
72
  TRILOGY_CAPABILITIES_t flags;
70
73
 
71
74
  size_t max_allowed_packet;
@@ -111,4 +114,20 @@ trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts);
111
114
  int trilogy_sock_resolve(trilogy_sock_t *raw);
112
115
  int trilogy_sock_upgrade_ssl(trilogy_sock_t *raw);
113
116
 
117
+ /* trilogy_sock_check - Verify if the socket is still alive and not disconnected.
118
+ *
119
+ * This check is very cheap to do and reduces the number of errors when for
120
+ * example the server has restarted since the connection was opened. In connection
121
+ * pooling implementations, this check can be done before the connection is
122
+ * returned.
123
+ *
124
+ * raw - A connected trilogy_sock_t pointer. Using a disconnected trilogy_sock_t is undefined.
125
+ *
126
+ * Return values:
127
+ * TRILOGY_OK - The connection is alive on the client side and can be.
128
+ * TRILOGY_CLOSED_CONNECTION - The connection is closed.
129
+ * TRILOGY_SYSERR - A system error occurred, check errno.
130
+ */
131
+ int trilogy_sock_check(trilogy_sock_t *raw);
132
+
114
133
  #endif
@@ -182,7 +182,7 @@ int trilogy_builder_write_buffer(trilogy_builder_t *builder, const void *data, s
182
182
 
183
183
  size_t fragment_remaining = TRILOGY_MAX_PACKET_LEN - builder->fragment_length;
184
184
 
185
- if (builder->packet_length >= builder->packet_max_length - len) {
185
+ if (builder->packet_length + len >= builder->packet_max_length) {
186
186
  return TRILOGY_MAX_PACKET_EXCEEDED;
187
187
  }
188
188
 
@@ -248,8 +248,9 @@ static int read_auth_switch_packet(trilogy_conn_t *conn, trilogy_handshake_t *ha
248
248
  }
249
249
 
250
250
  if (strcmp("mysql_native_password", auth_switch_packet.auth_plugin) &&
251
- strcmp("caching_sha2_password", auth_switch_packet.auth_plugin)) {
252
- // Only support native password & caching sha2 password here.
251
+ strcmp("caching_sha2_password", auth_switch_packet.auth_plugin) &&
252
+ strcmp("mysql_clear_password", auth_switch_packet.auth_plugin)) {
253
+ // Only support native password, caching sha2 and cleartext password here.
253
254
  return TRILOGY_PROTOCOL_VIOLATION;
254
255
  }
255
256
 
@@ -258,14 +259,7 @@ static int read_auth_switch_packet(trilogy_conn_t *conn, trilogy_handshake_t *ha
258
259
  return TRILOGY_AUTH_SWITCH;
259
260
  }
260
261
 
261
- static int read_generic_response(trilogy_conn_t *conn)
262
- {
263
- int rc = read_packet(conn);
264
-
265
- if (rc < 0) {
266
- return rc;
267
- }
268
-
262
+ static int handle_generic_response(trilogy_conn_t *conn) {
269
263
  switch (current_packet_type(conn)) {
270
264
  case TRILOGY_PACKET_OK:
271
265
  return read_ok_packet(conn);
@@ -278,6 +272,17 @@ static int read_generic_response(trilogy_conn_t *conn)
278
272
  }
279
273
  }
280
274
 
275
+ static int read_generic_response(trilogy_conn_t *conn)
276
+ {
277
+ int rc = read_packet(conn);
278
+
279
+ if (rc < 0) {
280
+ return rc;
281
+ }
282
+
283
+ return handle_generic_response(conn);
284
+ }
285
+
281
286
  int trilogy_connect_send(trilogy_conn_t *conn, const trilogy_sockopt_t *opts)
282
287
  {
283
288
  trilogy_sock_t *sock = trilogy_sock_new(opts);
@@ -300,14 +305,13 @@ int trilogy_connect_send_socket(trilogy_conn_t *conn, trilogy_sock_t *sock)
300
305
  return rc;
301
306
 
302
307
  conn->socket = sock;
308
+ conn->packet_parser.sequence_number = 0;
309
+
303
310
  return TRILOGY_OK;
304
311
  }
305
312
 
306
313
  int trilogy_connect_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake_out)
307
314
  {
308
- // reset the sequence number with each connect recv attempt
309
- conn->packet_parser.sequence_number = 0;
310
-
311
315
  int rc = read_packet(conn);
312
316
 
313
317
  if (rc < 0) {
@@ -336,9 +340,8 @@ int trilogy_connect_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake_ou
336
340
  int trilogy_auth_send(trilogy_conn_t *conn, const trilogy_handshake_t *handshake)
337
341
  {
338
342
  trilogy_builder_t builder;
339
- bool use_ssl = (conn->socket->opts.flags & TRILOGY_CAPABILITIES_SSL) != 0;
340
343
 
341
- int rc = begin_command_phase(&builder, conn, use_ssl ? 2 : 1);
344
+ int rc = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
342
345
 
343
346
  if (rc < 0) {
344
347
  return rc;
@@ -360,7 +363,7 @@ int trilogy_ssl_request_send(trilogy_conn_t *conn)
360
363
  {
361
364
  trilogy_builder_t builder;
362
365
 
363
- int rc = begin_command_phase(&builder, conn, 1);
366
+ int rc = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
364
367
 
365
368
  if (rc < 0) {
366
369
  return rc;
@@ -380,8 +383,7 @@ int trilogy_auth_switch_send(trilogy_conn_t *conn, const trilogy_handshake_t *ha
380
383
  {
381
384
  trilogy_builder_t builder;
382
385
 
383
- bool use_ssl = (conn->socket->opts.flags & TRILOGY_CAPABILITIES_SSL) != 0;
384
- int rc = begin_command_phase(&builder, conn, use_ssl ? 4 : 3);
386
+ int rc = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
385
387
 
386
388
  if (rc < 0) {
387
389
  return rc;
@@ -389,7 +391,7 @@ int trilogy_auth_switch_send(trilogy_conn_t *conn, const trilogy_handshake_t *ha
389
391
 
390
392
  rc = trilogy_build_auth_switch_response_packet(&builder, conn->socket->opts.password,
391
393
  conn->socket->opts.password_len, handshake->auth_plugin,
392
- handshake->scramble);
394
+ handshake->scramble, conn->socket->opts.enable_cleartext_plugin);
393
395
 
394
396
  if (rc < 0) {
395
397
  return rc;
@@ -405,6 +407,9 @@ void trilogy_auth_clear_password(trilogy_conn_t *conn)
405
407
  }
406
408
  }
407
409
 
410
+ #define FAST_AUTH_OK 3
411
+ #define FAST_AUTH_FAIL 4
412
+
408
413
  int trilogy_auth_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake)
409
414
  {
410
415
  int rc = read_packet(conn);
@@ -414,13 +419,69 @@ int trilogy_auth_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake)
414
419
  }
415
420
 
416
421
  switch (current_packet_type(conn)) {
417
- case TRILOGY_PACKET_OK:
418
- trilogy_auth_clear_password(conn);
419
- return read_ok_packet(conn);
422
+ case TRILOGY_PACKET_AUTH_MORE_DATA: {
423
+ bool use_ssl = (conn->socket->opts.flags & TRILOGY_CAPABILITIES_SSL) != 0;
424
+ bool has_unix_socket = (conn->socket->opts.path != NULL);
420
425
 
421
- case TRILOGY_PACKET_ERR:
426
+ if (!use_ssl && !has_unix_socket) {
427
+ return TRILOGY_UNSUPPORTED;
428
+ }
429
+
430
+ uint8_t byte = conn->packet_buffer.buff[1];
431
+ switch (byte) {
432
+ case FAST_AUTH_OK:
433
+ break;
434
+ case FAST_AUTH_FAIL:
435
+ {
436
+ trilogy_builder_t builder;
437
+ int err = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
438
+
439
+ if (err < 0) {
440
+ return err;
441
+ }
442
+
443
+ err = trilogy_build_auth_clear_password(&builder, conn->socket->opts.password, conn->socket->opts.password_len);
444
+
445
+ if (err < 0) {
446
+ return err;
447
+ }
448
+
449
+ int rc = begin_write(conn);
450
+
451
+ while (rc == TRILOGY_AGAIN) {
452
+ rc = trilogy_sock_wait_write(conn->socket);
453
+ if (rc != TRILOGY_OK) {
454
+ return rc;
455
+ }
456
+
457
+ rc = trilogy_flush_writes(conn);
458
+ }
459
+ if (rc != TRILOGY_OK) {
460
+ return rc;
461
+ }
462
+
463
+ break;
464
+ }
465
+ default:
466
+ return TRILOGY_UNEXPECTED_PACKET;
467
+ }
468
+ while (1) {
469
+ rc = read_packet(conn);
470
+
471
+ if (rc == TRILOGY_OK) {
472
+ break;
473
+ }
474
+ else if (rc == TRILOGY_AGAIN) {
475
+ rc = trilogy_sock_wait_read(conn->socket);
476
+ }
477
+
478
+ if (rc != TRILOGY_OK) {
479
+ return rc;
480
+ }
481
+ }
422
482
  trilogy_auth_clear_password(conn);
423
- return read_err_packet(conn);
483
+ return handle_generic_response(conn);
484
+ }
424
485
 
425
486
  case TRILOGY_PACKET_EOF:
426
487
  // EOF is returned here if an auth switch is requested.
@@ -428,9 +489,11 @@ int trilogy_auth_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake)
428
489
  // in a follow up call to this function after the switch.
429
490
  return read_auth_switch_packet(conn, handshake);
430
491
 
492
+ case TRILOGY_PACKET_OK:
493
+ case TRILOGY_PACKET_ERR:
431
494
  default:
432
495
  trilogy_auth_clear_password(conn);
433
- return TRILOGY_UNEXPECTED_PACKET;
496
+ return handle_generic_response(conn);
434
497
  }
435
498
 
436
499
  return read_generic_response(conn);
@@ -593,28 +593,48 @@ int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, cons
593
593
 
594
594
  trilogy_builder_finalize(builder);
595
595
 
596
- return TRILOGY_OK;
596
+ fail:
597
+ return rc;
598
+ }
599
+
600
+ int trilogy_build_auth_clear_password(trilogy_builder_t *builder, const char *pass, size_t pass_len) {
601
+ int rc = TRILOGY_OK;
602
+
603
+ CHECKED(trilogy_builder_write_buffer(builder, pass, pass_len));
604
+ CHECKED(trilogy_builder_write_uint8(builder, 0));
605
+ trilogy_builder_finalize(builder);
597
606
 
598
607
  fail:
599
608
  return rc;
600
609
  }
601
610
 
602
611
  int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const char *pass, size_t pass_len,
603
- const char *auth_plugin, const char *scramble)
612
+ const char *auth_plugin, const char *scramble, const bool enable_cleartext_plugin)
604
613
  {
605
614
  int rc = TRILOGY_OK;
606
615
  unsigned int auth_response_len = 0;
607
616
  uint8_t auth_response[EVP_MAX_MD_SIZE];
608
617
 
609
618
  if (pass_len > 0) {
610
- if (!strcmp("caching_sha2_password", auth_plugin)) {
611
- trilogy_pack_scramble_sha2_hash(scramble, pass, pass_len, auth_response, &auth_response_len);
619
+ if (!strcmp("mysql_clear_password", auth_plugin)) {
620
+ if (enable_cleartext_plugin) {
621
+ CHECKED(trilogy_builder_write_buffer(builder, pass, pass_len));
622
+ } else {
623
+ return TRILOGY_AUTH_PLUGIN_ERROR;
624
+ }
612
625
  } else {
613
- trilogy_pack_scramble_native_hash(scramble, pass, pass_len, auth_response, &auth_response_len);
626
+ if (!strcmp("caching_sha2_password", auth_plugin)) {
627
+ trilogy_pack_scramble_sha2_hash(scramble, pass, pass_len, auth_response, &auth_response_len);
628
+ } else if (!strcmp("mysql_native_password", auth_plugin)) {
629
+ trilogy_pack_scramble_native_hash(scramble, pass, pass_len, auth_response, &auth_response_len);
630
+ } else {
631
+ return TRILOGY_AUTH_PLUGIN_ERROR;
632
+ }
633
+
634
+ CHECKED(trilogy_builder_write_buffer(builder, auth_response, auth_response_len));
614
635
  }
615
636
  }
616
637
 
617
- CHECKED(trilogy_builder_write_buffer(builder, auth_response, auth_response_len));
618
638
  trilogy_builder_finalize(builder);
619
639
 
620
640
  return TRILOGY_OK;
@@ -906,6 +926,8 @@ int trilogy_build_stmt_execute_packet(trilogy_builder_t *builder, uint32_t stmt_
906
926
  case TRILOGY_TYPE_VAR_STRING:
907
927
  case TRILOGY_TYPE_STRING:
908
928
  case TRILOGY_TYPE_GEOMETRY:
929
+ case TRILOGY_TYPE_JSON:
930
+ case TRILOGY_TYPE_VECTOR:
909
931
  CHECKED(trilogy_builder_write_lenenc_buffer(builder, val.as.str.data, val.as.str.len));
910
932
 
911
933
  break;
@@ -1039,6 +1061,7 @@ int trilogy_parse_stmt_row_packet(const uint8_t *buff, size_t len, trilogy_colum
1039
1061
  case TRILOGY_TYPE_DECIMAL:
1040
1062
  case TRILOGY_TYPE_NEWDECIMAL:
1041
1063
  case TRILOGY_TYPE_JSON:
1064
+ case TRILOGY_TYPE_VECTOR:
1042
1065
  CHECKED(trilogy_reader_get_lenenc_buffer(&reader, &out_values[i].as.str.len,
1043
1066
  (const void **)&out_values[i].as.str.data));
1044
1067
 
@@ -40,6 +40,7 @@ static int _cb_wait(trilogy_sock_t *_sock, trilogy_wait_t wait)
40
40
  case TRILOGY_WAIT_READ:
41
41
  pfd.events = POLLIN;
42
42
  break;
43
+ case TRILOGY_WAIT_CONNECT:
43
44
  case TRILOGY_WAIT_WRITE:
44
45
  pfd.events = POLLOUT;
45
46
  break;
@@ -252,7 +253,7 @@ static int raw_connect_internal(struct trilogy_sock *sock, const struct addrinfo
252
253
  }
253
254
  }
254
255
 
255
- if ((rc = trilogy_sock_wait_write((trilogy_sock_t *)sock)) < 0) {
256
+ if ((rc = trilogy_sock_wait((trilogy_sock_t *)sock, TRILOGY_WAIT_CONNECT)) < 0) {
256
257
  goto failrc;
257
258
  }
258
259
 
@@ -724,3 +725,25 @@ fail:
724
725
  sock->ssl = NULL;
725
726
  return TRILOGY_OPENSSL_ERR;
726
727
  }
728
+
729
+ int trilogy_sock_check(trilogy_sock_t *_sock)
730
+ {
731
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
732
+ char buf[1];
733
+ while (1) {
734
+ ssize_t data_read = recv(sock->fd, buf, 1, MSG_PEEK);
735
+ if (data_read > 0) {
736
+ return TRILOGY_OK;
737
+ }
738
+ if (data_read == 0) {
739
+ return TRILOGY_CLOSED_CONNECTION;
740
+ }
741
+ if (errno == EINTR) {
742
+ continue;
743
+ }
744
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
745
+ return TRILOGY_OK;
746
+ }
747
+ return TRILOGY_SYSERR;
748
+ }
749
+ }
data/lib/trilogy/error.rb CHANGED
@@ -12,7 +12,7 @@ class Trilogy
12
12
  end
13
13
 
14
14
  # Trilogy may raise various syscall errors, which we treat as Trilogy::Errors.
15
- class SyscallError
15
+ module SyscallError
16
16
  ERRORS = {}
17
17
 
18
18
  Errno.constants
@@ -20,7 +20,10 @@ class Trilogy
20
20
  .select { |c| c.is_a?(Class) && c < SystemCallError }
21
21
  .each do |c|
22
22
  errno_name = c.to_s.split('::').last
23
- ERRORS[c::Errno] = const_set(errno_name, Class.new(c) { include Trilogy::ConnectionError })
23
+ ERRORS[c::Errno] = const_set(errno_name, Class.new(c) {
24
+ include Trilogy::ConnectionError
25
+ singleton_class.define_method(:===, Module.instance_method(:===))
26
+ })
24
27
  end
25
28
 
26
29
  ERRORS.freeze
@@ -32,6 +35,11 @@ class Trilogy
32
35
  end
33
36
  end
34
37
 
38
+ ConnectionRefusedError = SyscallError::ECONNREFUSED
39
+ deprecate_constant :ConnectionRefusedError
40
+ ConnectionResetError = SyscallError::ECONNRESET
41
+ deprecate_constant :ConnectionResetError
42
+
35
43
  class BaseError < StandardError
36
44
  include Error
37
45
 
@@ -58,21 +66,7 @@ class Trilogy
58
66
  class CastError < ClientError
59
67
  end
60
68
 
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
69
+ class TimeoutError < BaseConnectionError
76
70
  end
77
71
 
78
72
  # DatabaseError was replaced by ProtocolError, but we'll keep it around as an
@@ -121,4 +115,9 @@ class Trilogy
121
115
  # attempted on a socket which previously encountered an error.
122
116
  class EOFError < BaseConnectionError
123
117
  end
118
+
119
+ # Occurs when the server request an auth switch to an incompatible
120
+ # authentication plugin
121
+ class AuthPluginError < Trilogy::BaseConnectionError
122
+ end
124
123
  end
@@ -1,3 +1,3 @@
1
1
  class Trilogy
2
- VERSION = "2.6.1"
2
+ VERSION = "2.9.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trilogy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.1
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-04 00:00:00.000000000 Z
11
+ date: 2024-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -102,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  requirements: []
105
- rubygems_version: 3.4.10
105
+ rubygems_version: 3.5.11
106
106
  signing_key:
107
107
  specification_version: 4
108
108
  summary: A friendly MySQL-compatible library for Ruby, binding to libtrilogy