trilogy 2.6.1 → 2.8.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: dc0ed0331bd27abe2cf82b8c1e334dbd0d68ac93ae2bbd73fac5fe12874ee324
4
+ data.tar.gz: 457d93059c16e539c9b79f901269476f9380b0141e13edfd0624a0de6b5e06d1
5
5
  SHA512:
6
- metadata.gz: b6e50321b8493b6ea8137d534e0fa8eedc5b7388093fee5e910dbe8c6bd81c0e0dc9c8d9c448895096fb8bd0244a0ee65b19b5f580c3b9137c76c9af221f0ab2
7
- data.tar.gz: 7d8b81caf8faaadc4ef548ffddab7a73f9b8d4d6ede5a0f9078c6ea65cd195f83bdcacdf5a3d7b71d76503161a689d8ac66b058a4c605a915f9a9dcf4aa56cdd
6
+ metadata.gz: d77b2c84b0fec65762a591e3685ac50f8a031cb74a7fee90ca4b1bf02173a8abd2d6c7b9be410c0bab7b71b6e85c5c13de8db21bddb93eb265aeeda395184f46
7
+ data.tar.gz: 668737a977f8d831fbb1509e4e9695e0d3ca83e7a60859a485d3ae7bed0d4c2659994fd0d33ac12cf73a74b09400a3ef7ee631e9c7bc3cccfcfb70b19dff745b
@@ -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);
@@ -160,6 +156,14 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
160
156
  rb_raise(Trilogy_EOFError, "%" PRIsVALUE ": TRILOGY_CLOSED_CONNECTION", rbmsg);
161
157
  }
162
158
 
159
+ case TRILOGY_AUTH_PLUGIN_ERROR: {
160
+ rb_raise(Trilogy_AuthPluginError, "%" PRIsVALUE ": TRILOGY_AUTH_PLUGIN_ERROR", rbmsg);
161
+ }
162
+
163
+ case TRILOGY_UNSUPPORTED: {
164
+ rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE ": TRILOGY_UNSUPPORTED", rbmsg);
165
+ }
166
+
163
167
  default:
164
168
  rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
165
169
  }
@@ -243,7 +247,18 @@ static int _cb_ruby_wait(trilogy_sock_t *sock, trilogy_wait_t wait)
243
247
  wait_flag = RB_WAITFD_OUT;
244
248
  break;
245
249
 
250
+ case TRILOGY_WAIT_CONNECT:
251
+ // wait for connection to be writable
252
+ timeout = &sock->opts.connect_timeout;
253
+ if (timeout->tv_sec == 0 && timeout->tv_usec == 0) {
254
+ // We used to use the write timeout for this, so if a connect timeout isn't configured, default to that.
255
+ timeout = &sock->opts.write_timeout;
256
+ }
257
+ wait_flag = RB_WAITFD_OUT;
258
+ break;
259
+
246
260
  case TRILOGY_WAIT_HANDSHAKE:
261
+ // wait for handshake packet on initial connection
247
262
  timeout = &sock->opts.connect_timeout;
248
263
  wait_flag = RB_WAITFD_IN;
249
264
  break;
@@ -412,7 +427,12 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
412
427
  }
413
428
 
414
429
  if (rc != TRILOGY_AGAIN) {
415
- handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
430
+ if (rc == TRILOGY_UNSUPPORTED) {
431
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv: caching_sha2_password requires either TCP with TLS or a unix socket");
432
+ }
433
+ else {
434
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
435
+ }
416
436
  }
417
437
 
418
438
  rc = trilogy_sock_wait_read(ctx->conn.socket);
@@ -515,6 +535,10 @@ static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE
515
535
  connopt.flags |= TRILOGY_CAPABILITIES_CONNECT_WITH_DB;
516
536
  }
517
537
 
538
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_enable_cleartext_plugin)))) {
539
+ connopt.enable_cleartext_plugin = true;
540
+ }
541
+
518
542
  if (RTEST(rb_hash_aref(opts, ID2SYM(id_found_rows)))) {
519
543
  connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
520
544
  }
@@ -1018,6 +1042,17 @@ static VALUE rb_trilogy_closed(VALUE self)
1018
1042
  }
1019
1043
  }
1020
1044
 
1045
+ static VALUE rb_trilogy_check(VALUE self)
1046
+ {
1047
+ struct trilogy_ctx *ctx = get_open_ctx(self);
1048
+
1049
+ int rc = trilogy_sock_check(ctx->conn.socket);
1050
+ if (rc != TRILOGY_OK && rc != TRILOGY_AGAIN) {
1051
+ handle_trilogy_error(ctx, rc, "trilogy_sock_check");
1052
+ }
1053
+ return Qtrue;
1054
+ }
1055
+
1021
1056
  static VALUE rb_trilogy_discard(VALUE self)
1022
1057
  {
1023
1058
  struct trilogy_ctx *ctx = get_ctx(self);
@@ -1096,7 +1131,7 @@ static VALUE rb_trilogy_server_status(VALUE self) { return LONG2FIX(get_open_ctx
1096
1131
 
1097
1132
  static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_open_ctx(self)->server_version); }
1098
1133
 
1099
- RUBY_FUNC_EXPORTED void Init_cext()
1134
+ RUBY_FUNC_EXPORTED void Init_cext(void)
1100
1135
  {
1101
1136
  VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
1102
1137
  rb_define_alloc_func(Trilogy, allocate_trilogy);
@@ -1109,6 +1144,7 @@ RUBY_FUNC_EXPORTED void Init_cext()
1109
1144
  rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
1110
1145
  rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
1111
1146
  rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
1147
+ rb_define_method(Trilogy, "check", rb_trilogy_check, 0);
1112
1148
  rb_define_method(Trilogy, "discard!", rb_trilogy_discard, 0);
1113
1149
  rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
1114
1150
  rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);
@@ -1156,12 +1192,6 @@ RUBY_FUNC_EXPORTED void Init_cext()
1156
1192
  Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
1157
1193
  rb_global_variable(&Trilogy_TimeoutError);
1158
1194
 
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
1195
  Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
1166
1196
  rb_global_variable(&Trilogy_BaseConnectionError);
1167
1197
 
@@ -1180,6 +1210,9 @@ RUBY_FUNC_EXPORTED void Init_cext()
1180
1210
  Trilogy_EOFError = rb_const_get(Trilogy, rb_intern("EOFError"));
1181
1211
  rb_global_variable(&Trilogy_EOFError);
1182
1212
 
1213
+ rb_global_variable(&Trilogy_AuthPluginError);
1214
+ Trilogy_AuthPluginError = rb_const_get(Trilogy, rb_intern("AuthPluginError"));
1215
+
1183
1216
  id_socket = rb_intern("socket");
1184
1217
  id_host = rb_intern("host");
1185
1218
  id_port = rb_intern("port");
@@ -1195,6 +1228,7 @@ RUBY_FUNC_EXPORTED void Init_cext()
1195
1228
  id_keepalive_count = rb_intern("keepalive_count");
1196
1229
  id_keepalive_interval = rb_intern("keepalive_interval");
1197
1230
  id_database = rb_intern("database");
1231
+ id_enable_cleartext_plugin = rb_intern("enable_cleartext_plugin");
1198
1232
  id_ssl_ca = rb_intern("ssl_ca");
1199
1233
  id_ssl_capath = rb_intern("ssl_capath");
1200
1234
  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,
@@ -367,6 +367,7 @@ typedef enum {
367
367
  // Typical response packet types
368
368
  typedef enum {
369
369
  TRILOGY_PACKET_OK = 0x0,
370
+ TRILOGY_PACKET_AUTH_MORE_DATA = 0x01,
370
371
  TRILOGY_PACKET_EOF = 0xfe,
371
372
  TRILOGY_PACKET_ERR = 0xff,
372
373
  TRILOGY_PACKET_UNKNOWN
@@ -442,14 +443,16 @@ int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, cons
442
443
  * pass_len - The length of password in bytes.
443
444
  * auth_plugin - Plugin authentication mechanism that the server requested.
444
445
  * scramble - The scramble value received from the server.
446
+ * enable_cleartext_plugin - Send cleartext password if requested by server.
445
447
  *
446
448
  * Return values:
447
449
  * TRILOGY_OK - The packet was successfully built and written to the
448
450
  * builder's internal buffer.
449
451
  * TRILOGY_SYSERR - A system error occurred, check errno.
452
+ * TRILOGY_AUTH_PLUGIN_ERROR - The server requested auth plugin is not supported.
450
453
  */
451
454
  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);
455
+ const char *auth_plugin, const char *scramble, const bool enable_cleartext_plugin);
453
456
 
454
457
  /* trilogy_build_change_db_packet - Build a change database command packet. This
455
458
  * command will change the default database for the connection.
@@ -1035,4 +1038,6 @@ int trilogy_parse_stmt_ok_packet(const uint8_t *buff, size_t len, trilogy_stmt_o
1035
1038
  int trilogy_parse_stmt_row_packet(const uint8_t *buff, size_t len, trilogy_column_packet_t *columns,
1036
1039
  uint64_t column_count, trilogy_binary_value_t *out_values);
1037
1040
 
1041
+ int trilogy_build_auth_clear_password(trilogy_builder_t *builder, const char *pass, size_t pass_len);
1042
+
1038
1043
  #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
@@ -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);
@@ -336,9 +341,8 @@ int trilogy_connect_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake_ou
336
341
  int trilogy_auth_send(trilogy_conn_t *conn, const trilogy_handshake_t *handshake)
337
342
  {
338
343
  trilogy_builder_t builder;
339
- bool use_ssl = (conn->socket->opts.flags & TRILOGY_CAPABILITIES_SSL) != 0;
340
344
 
341
- int rc = begin_command_phase(&builder, conn, use_ssl ? 2 : 1);
345
+ int rc = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
342
346
 
343
347
  if (rc < 0) {
344
348
  return rc;
@@ -360,7 +364,7 @@ int trilogy_ssl_request_send(trilogy_conn_t *conn)
360
364
  {
361
365
  trilogy_builder_t builder;
362
366
 
363
- int rc = begin_command_phase(&builder, conn, 1);
367
+ int rc = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
364
368
 
365
369
  if (rc < 0) {
366
370
  return rc;
@@ -380,8 +384,7 @@ int trilogy_auth_switch_send(trilogy_conn_t *conn, const trilogy_handshake_t *ha
380
384
  {
381
385
  trilogy_builder_t builder;
382
386
 
383
- bool use_ssl = (conn->socket->opts.flags & TRILOGY_CAPABILITIES_SSL) != 0;
384
- int rc = begin_command_phase(&builder, conn, use_ssl ? 4 : 3);
387
+ int rc = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
385
388
 
386
389
  if (rc < 0) {
387
390
  return rc;
@@ -389,7 +392,7 @@ int trilogy_auth_switch_send(trilogy_conn_t *conn, const trilogy_handshake_t *ha
389
392
 
390
393
  rc = trilogy_build_auth_switch_response_packet(&builder, conn->socket->opts.password,
391
394
  conn->socket->opts.password_len, handshake->auth_plugin,
392
- handshake->scramble);
395
+ handshake->scramble, conn->socket->opts.enable_cleartext_plugin);
393
396
 
394
397
  if (rc < 0) {
395
398
  return rc;
@@ -405,6 +408,9 @@ void trilogy_auth_clear_password(trilogy_conn_t *conn)
405
408
  }
406
409
  }
407
410
 
411
+ #define FAST_AUTH_OK 3
412
+ #define FAST_AUTH_FAIL 4
413
+
408
414
  int trilogy_auth_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake)
409
415
  {
410
416
  int rc = read_packet(conn);
@@ -414,13 +420,69 @@ int trilogy_auth_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake)
414
420
  }
415
421
 
416
422
  switch (current_packet_type(conn)) {
417
- case TRILOGY_PACKET_OK:
418
- trilogy_auth_clear_password(conn);
419
- return read_ok_packet(conn);
423
+ case TRILOGY_PACKET_AUTH_MORE_DATA: {
424
+ bool use_ssl = (conn->socket->opts.flags & TRILOGY_CAPABILITIES_SSL) != 0;
425
+ bool has_unix_socket = (conn->socket->opts.path != NULL);
420
426
 
421
- case TRILOGY_PACKET_ERR:
427
+ if (!use_ssl && !has_unix_socket) {
428
+ return TRILOGY_UNSUPPORTED;
429
+ }
430
+
431
+ uint8_t byte = conn->packet_buffer.buff[1];
432
+ switch (byte) {
433
+ case FAST_AUTH_OK:
434
+ break;
435
+ case FAST_AUTH_FAIL:
436
+ {
437
+ trilogy_builder_t builder;
438
+ int err = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
439
+
440
+ if (err < 0) {
441
+ return err;
442
+ }
443
+
444
+ err = trilogy_build_auth_clear_password(&builder, conn->socket->opts.password, conn->socket->opts.password_len);
445
+
446
+ if (err < 0) {
447
+ return err;
448
+ }
449
+
450
+ int rc = begin_write(conn);
451
+
452
+ while (rc == TRILOGY_AGAIN) {
453
+ rc = trilogy_sock_wait_write(conn->socket);
454
+ if (rc != TRILOGY_OK) {
455
+ return rc;
456
+ }
457
+
458
+ rc = trilogy_flush_writes(conn);
459
+ }
460
+ if (rc != TRILOGY_OK) {
461
+ return rc;
462
+ }
463
+
464
+ break;
465
+ }
466
+ default:
467
+ return TRILOGY_UNEXPECTED_PACKET;
468
+ }
469
+ while (1) {
470
+ rc = read_packet(conn);
471
+
472
+ if (rc == TRILOGY_OK) {
473
+ break;
474
+ }
475
+ else if (rc == TRILOGY_AGAIN) {
476
+ rc = trilogy_sock_wait_read(conn->socket);
477
+ }
478
+
479
+ if (rc != TRILOGY_OK) {
480
+ return rc;
481
+ }
482
+ }
422
483
  trilogy_auth_clear_password(conn);
423
- return read_err_packet(conn);
484
+ return handle_generic_response(conn);
485
+ }
424
486
 
425
487
  case TRILOGY_PACKET_EOF:
426
488
  // EOF is returned here if an auth switch is requested.
@@ -428,9 +490,11 @@ int trilogy_auth_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake)
428
490
  // in a follow up call to this function after the switch.
429
491
  return read_auth_switch_packet(conn, handshake);
430
492
 
493
+ case TRILOGY_PACKET_OK:
494
+ case TRILOGY_PACKET_ERR:
431
495
  default:
432
496
  trilogy_auth_clear_password(conn);
433
- return TRILOGY_UNEXPECTED_PACKET;
497
+ return handle_generic_response(conn);
434
498
  }
435
499
 
436
500
  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;
@@ -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.8.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.8.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-04-09 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.3
106
106
  signing_key:
107
107
  specification_version: 4
108
108
  summary: A friendly MySQL-compatible library for Ruby, binding to libtrilogy