trilogy 2.6.1 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
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