diff --git a/router/src/routing/src/CMakeLists.txt b/router/src/routing/src/CMakeLists.txt index faf044fd1cb8..ea1f96238eb4 100644 --- a/router/src/routing/src/CMakeLists.txt +++ b/router/src/routing/src/CMakeLists.txt @@ -61,36 +61,54 @@ ADD_LIBRARY(routing SHARED classic_auth.cc classic_auth_cleartext.cc classic_auth_caching_sha2.cc - classic_auth_forwarder.cc classic_auth_native.cc classic_auth_sha256_password.cc - classic_binlog_dump.cc - classic_change_user.cc - classic_clone.cc classic_command.cc classic_connect.cc classic_flow.cc classic_forwarder.cc classic_frame.cc - classic_greeting.cc - classic_init_schema.cc - classic_kill.cc classic_lazy_connect.cc - classic_list_fields.cc - classic_ping.cc - classic_query.cc - classic_quit.cc - classic_register_replica.cc - classic_reload.cc - classic_reset_connection.cc - classic_set_option.cc - classic_statistics.cc - classic_stmt_close.cc - classic_stmt_execute.cc - classic_stmt_fetch.cc - classic_stmt_param_append_data.cc - classic_stmt_prepare.cc - classic_stmt_reset.cc + + classic_auth_cleartext_forwarder.cc + classic_auth_caching_sha2_forwarder.cc + classic_auth_forwarder.cc + classic_auth_native_forwarder.cc + classic_auth_sha256_password_forwarder.cc + classic_binlog_dump_forwarder.cc + classic_change_user_forwarder.cc + classic_clone_forwarder.cc + classic_greeting_forwarder.cc + classic_init_schema_forwarder.cc + classic_kill_forwarder.cc + classic_list_fields_forwarder.cc + classic_ping_forwarder.cc + classic_query_forwarder.cc + classic_quit_forwarder.cc + classic_register_replica_forwarder.cc + classic_reload_forwarder.cc + classic_reset_connection_forwarder.cc + classic_set_option_forwarder.cc + classic_statistics_forwarder.cc + classic_stmt_close_forwarder.cc + classic_stmt_execute_forwarder.cc + classic_stmt_fetch_forwarder.cc + classic_stmt_param_append_data_forwarder.cc + classic_stmt_prepare_forwarder.cc + classic_stmt_reset_forwarder.cc + + classic_auth_cleartext_sender.cc + classic_auth_caching_sha2_sender.cc + classic_auth_native_sender.cc + classic_auth_sha256_password_sender.cc + classic_change_user_sender.cc + # classic_greeting_sender.cc + classic_init_schema_sender.cc + classic_query_sender.cc + classic_quit_sender.cc + classic_reset_connection_sender.cc + + classic_greeting_receiver.cc sql_value.cc ) diff --git a/router/src/routing/src/classic_auth_caching_sha2.cc b/router/src/routing/src/classic_auth_caching_sha2.cc index 5d2548c655d0..c012b4bd7a8d 100644 --- a/router/src/routing/src/classic_auth_caching_sha2.cc +++ b/router/src/routing/src/classic_auth_caching_sha2.cc @@ -27,15 +27,8 @@ #include "auth_digest.h" #include "classic_frame.h" #include "harness_assert.h" -#include "hexify.h" -#include "mysql/harness/logging/logging.h" #include "mysql/harness/stdx/expected.h" #include "mysqld_error.h" // mysql-server error-codes -#include "mysqlrouter/classic_protocol_wire.h" - -IMPORT_LOG_FUNCTIONS() - -using mysql_harness::hexify; // AuthCachingSha2Password @@ -98,767 +91,3 @@ bool AuthCachingSha2Password::is_public_key_request( bool AuthCachingSha2Password::is_public_key(const std::string_view &data) { return data.size() == 256; } - -// sender - -stdx::expected -AuthCachingSha2Sender::process() { - switch (stage()) { - case Stage::Init: - return init(); - case Stage::Response: - return response(); - case Stage::PublicKey: - return public_key(); - case Stage::AuthData: - return auth_data(); - case Stage::Error: - return error(); - case Stage::Ok: - return ok(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected -AuthCachingSha2Sender::init() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - auto scramble_res = Auth::scramble( - AuthBase::strip_trailing_null(initial_server_auth_data_), password_); - if (!scramble_res) { - return send_server_failed(make_error_code(std::errc::invalid_argument)); - } - - auto send_res = ClassicFrame::send_msg( - dst_channel, dst_protocol, - classic_protocol::borrowed::message::client::AuthMethodData{ - *scramble_res}); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - - return Result::SendToServer; -} - -stdx::expected -AuthCachingSha2Sender::send_password() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - if (socket_splicer->server_conn().is_secure_transport()) { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::sender::plaintext_password")); - } - auto send_res = ClassicFrame::send_msg( - dst_channel, dst_protocol, - classic_protocol::borrowed::message::client::AuthMethodData{password_ + - '\0'}); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - } else { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::sender::public_key_request")); - } - - auto send_res = Auth::send_public_key_request(dst_channel, dst_protocol); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::PublicKey); - } - - return Result::SendToServer; -} - -stdx::expected -AuthCachingSha2Sender::response() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Ok = ClassicFrame::cmd_byte(), - Error = ClassicFrame::cmd_byte(), - AuthData = ClassicFrame::cmd_byte< - classic_protocol::message::server::AuthMethodData>(), - }; - - switch (Msg{msg_type}) { - case Msg::AuthData: - stage(Stage::AuthData); - return Result::Again; - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug("received unexpected message from server in caching-sha2-auth:\n%s", - hexify(recv_buf).c_str()); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -stdx::expected -AuthCachingSha2Sender::public_key() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::server::AuthMethodData>( - dst_channel, dst_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - auto msg = *msg_res; - - auto pubkey_res = Auth::public_key_from_pem(msg.auth_method_data()); - if (!pubkey_res) return recv_server_failed(pubkey_res.error()); - - // discard _after_ msg. is used as msg borrows from the dst_channel's - // recv_buffer. - discard_current_msg(dst_channel, dst_protocol); - - auto nonce = initial_server_auth_data_; - - // if there is a trailing zero, strip it. - if (nonce.size() == Auth::kNonceLength + 1 && - nonce[Auth::kNonceLength] == 0x00) { - nonce = nonce.substr(0, Auth::kNonceLength); - } - - auto encrypted_res = - Auth::rsa_encrypt_password(*pubkey_res, password_, nonce); - if (!encrypted_res) return send_server_failed(encrypted_res.error()); - - auto send_res = - Auth::send_encrypted_password(dst_channel, dst_protocol, *encrypted_res); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - - return Result::SendToServer; -} - -stdx::expected -AuthCachingSha2Sender::auth_data() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::server::AuthMethodData>( - dst_channel, dst_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (msg_res->auth_method_data() == std::string_view("\x04")) { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::sender::request_full_auth")); - } - - discard_current_msg(dst_channel, dst_protocol); - - return send_password(); - } else if (msg_res->auth_method_data() == std::string_view("\x03")) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::sender::fast_auth_ok")); - } - - // as the client did the slow path, it doesn't expect a fast-auth-ok. - discard_current_msg(dst_channel, dst_protocol); - - // next should be an Ok - stage(Stage::Response); - - return Result::Again; - } else { - return recv_server_failed(make_error_code(std::errc::bad_message)); - } -} - -stdx::expected AuthCachingSha2Sender::ok() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::sender::ok")); - } - - return Result::Again; -} - -stdx::expected -AuthCachingSha2Sender::error() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::sender::error")); - } - - return Result::Again; -} - -// Forwarder - -stdx::expected -AuthCachingSha2Forwarder::process() { - switch (stage()) { - case Stage::Init: - return init(); - case Stage::ClientData: - return client_data(); - case Stage::EncryptedPassword: - return encrypted_password(); - case Stage::PlaintextPassword: - return plaintext_password(); - case Stage::Response: - return response(); - case Stage::PublicKeyResponse: - return public_key_response(); - case Stage::PublicKey: - return public_key(); - case Stage::AuthData: - return auth_data(); - case Stage::Error: - return error(); - case Stage::Ok: - return ok(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected -AuthCachingSha2Forwarder::init() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->client_channel(); - auto dst_protocol = connection()->client_protocol(); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::switch")); - } - - auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::AuthMethodSwitch>( - dst_channel, dst_protocol, {Auth::kName, initial_server_auth_data_}); - if (!send_res) return send_client_failed(send_res.error()); - - stage(Stage::ClientData); - return Result::SendToClient; -} - -stdx::expected -AuthCachingSha2Forwarder::client_data() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->client_channel(); - auto src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::client::AuthMethodData>( - src_channel, src_protocol); - if (!msg_res) return recv_client_failed(msg_res.error()); - - if (Auth::is_public_key_request(msg_res->auth_method_data())) { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::forward::public_key_request")); - } - - if (AuthBase::connection_has_public_key(connection())) { - // send the router's public-key to be able to decrypt the client's - // password. - discard_current_msg(src_channel, src_protocol); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::public_key")); - } - - auto pubkey_res = Auth::public_key_from_ssl_ctx_as_pem( - connection()->context().source_ssl_ctx()->get()); - if (!pubkey_res) { - auto ec = pubkey_res.error(); - - if (ec != std::errc::function_not_supported) { - return send_client_failed(ec); - } - - stage(Stage::Done); - - // couldn't get the public key, fail the auth. - auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::Error>( - src_channel, src_protocol, - {ER_ACCESS_DENIED_ERROR, "Access denied", "HY000"}); - if (!send_res) return send_client_failed(send_res.error()); - } else { - // send the router's public key to the client. - stage(Stage::EncryptedPassword); - - auto send_res = - Auth::send_public_key(src_channel, src_protocol, *pubkey_res); - if (!send_res) return send_client_failed(send_res.error()); - } - - return Result::SendToClient; - } else { - // client requested a public key, but router has no ssl-ctx - // (client-ssl-mode is DISABLED|PASSTHROUGH) - // - // If the server-connection is secure, the server will treat the - // public-key-request as an invalid password - // (as it isn't terminated by \0) - stage(Stage::PublicKeyResponse); - - return forward_client_to_server(); - } - } else { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::forward::scrambled_password")); - } - - // if it isn't a public-key request, it is a fast-auth. - stage(Stage::Response); - - return forward_client_to_server(); - } -} - -// encrypted password from client to server. -stdx::expected -AuthCachingSha2Forwarder::encrypted_password() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->client_channel(); - auto src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::client::AuthMethodData>( - src_channel, src_protocol); - if (!msg_res) return recv_client_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::encrypted")); - } - - if (AuthBase::connection_has_public_key(connection())) { - auto nonce = initial_server_auth_data_; - - // if there is a trailing zero, strip it. - if (nonce.size() == Auth::kNonceLength + 1 && - nonce[Auth::kNonceLength] == 0x00) { - nonce = nonce.substr(0, Auth::kNonceLength); - } - - auto recv_res = Auth::rsa_decrypt_password( - connection()->context().source_ssl_ctx()->get(), - msg_res->auth_method_data(), nonce); - if (!recv_res) return recv_client_failed(recv_res.error()); - - src_protocol->password(*recv_res); - - discard_current_msg(src_channel, src_protocol); - - return send_password(); - } else { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::encrypted")); - } - - stage(Stage::Response); - - return forward_client_to_server(); - } -} - -// plaintext password from client to server. -stdx::expected -AuthCachingSha2Forwarder::plaintext_password() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->client_channel(); - auto src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::client::AuthMethodData>( - src_channel, src_protocol); - if (!msg_res) return recv_client_failed(msg_res.error()); - - if (socket_splicer->client_conn().is_secure_transport()) { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::forward::plaintext_password")); - } - - // remove trailing null - src_protocol->password(std::string( - AuthBase::strip_trailing_null(msg_res->auth_method_data()))); - - discard_current_msg(src_channel, src_protocol); - - return send_password(); - } else if (Auth::is_public_key_request(msg_res->auth_method_data())) { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::forward::public_key_request")); - } - - if (AuthBase::connection_has_public_key(connection())) { - // send the router's public-key to be able to decrypt the client's - // password. - discard_current_msg(src_channel, src_protocol); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::public_key")); - } - - auto pubkey_res = Auth::public_key_from_ssl_ctx_as_pem( - connection()->context().source_ssl_ctx()->get()); - if (!pubkey_res) { - auto ec = pubkey_res.error(); - - if (ec != std::errc::function_not_supported) { - return send_client_failed(ec); - } - - stage(Stage::Done); - - // couldn't get the public key, fail the auth. - auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::Error>( - src_channel, src_protocol, - {ER_ACCESS_DENIED_ERROR, "Access denied", "HY000"}); - if (!send_res) return send_client_failed(send_res.error()); - } else { - // send the router's public key to the client. - stage(Stage::EncryptedPassword); - - auto send_res = - Auth::send_public_key(src_channel, src_protocol, *pubkey_res); - if (!send_res) return send_client_failed(send_res.error()); - } - - return Result::SendToClient; - } else { - // client requested a public key, but router has no ssl-ctx - // (client-ssl-mode is DISABLED|PASSTHROUGH) - // - // If the server-connection is encrypted, the server will treat the - // public-key-request as an invalid password - // (as it isn't terminated by \0) - stage(Stage::PublicKeyResponse); - - return forward_client_to_server(); - } - } else { - discard_current_msg(src_channel, src_protocol); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::bad_message")); - } - - return recv_client_failed(make_error_code(std::errc::bad_message)); - } -} - -// encrypted password from client to server. -stdx::expected -AuthCachingSha2Forwarder::send_password() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_protocol = connection()->client_protocol(); - - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - if (socket_splicer->server_conn().is_secure_transport()) { - // the server-side is secure: send plaintext password - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::forward::plaintext_password")); - } - - stage(Stage::Response); - - auto send_res = Auth::send_plaintext_password( - dst_channel, dst_protocol, src_protocol->password().value()); - if (!send_res) return send_server_failed(send_res.error()); - } else { - // the server is NOT secure: ask for the server's publickey - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::forward::public_key_request")); - } - - stage(Stage::PublicKeyResponse); - - auto send_res = Auth::send_public_key_request(dst_channel, dst_protocol); - if (!send_res) return send_server_failed(send_res.error()); - } - - return Result::SendToServer; -} - -stdx::expected -AuthCachingSha2Forwarder::response() { - // ERR|OK|EOF|other - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Ok = ClassicFrame::cmd_byte(), - Error = ClassicFrame::cmd_byte(), - AuthData = ClassicFrame::cmd_byte< - classic_protocol::message::server::AuthMethodData>(), - }; - - switch (Msg{msg_type}) { - case Msg::AuthData: - stage(Stage::AuthData); - return Result::Again; - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug("received unexpected message from server in caching-sha2-auth:\n%s", - hexify(recv_buf).c_str()); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -stdx::expected -AuthCachingSha2Forwarder::public_key_response() { - // ERR|OK|EOF|other - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - AuthData = ClassicFrame::cmd_byte< - classic_protocol::message::server::AuthMethodData>(), - Error = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::AuthData: - stage(Stage::PublicKey); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::forward::public_key_response")); - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug( - "received unexpected message from server in sha256-password-auth:\n%s", - hexify(recv_buf).c_str()); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -// receive a public-key from the server. -stdx::expected -AuthCachingSha2Forwarder::public_key() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - auto src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::server::AuthMethodData>( - dst_channel, dst_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::public-key")); - } - - if (!src_protocol->password().has_value()) { - // the client's password isn't known. - // - // Forward the server's public-key to the client - stage(Stage::EncryptedPassword); - - return forward_server_to_client(); - } - - // as the plaintext password is known, encrypt it with the server's - // public-key. - auto pubkey_res = Auth::public_key_from_pem(msg_res->auth_method_data()); - if (!pubkey_res) return recv_server_failed(pubkey_res.error()); - - discard_current_msg(dst_channel, dst_protocol); - - auto password = *src_protocol->password(); - - auto nonce = initial_server_auth_data_; - - // if there is a trailing zero, strip it. - if (nonce.size() == Auth::kNonceLength + 1 && - nonce[Auth::kNonceLength] == 0x00) { - nonce = nonce.substr(0, Auth::kNonceLength); - } - - auto encrypted_res = Auth::rsa_encrypt_password(*pubkey_res, password, nonce); - if (!encrypted_res) return send_server_failed(encrypted_res.error()); - - auto send_res = - Auth::send_encrypted_password(dst_channel, dst_protocol, *encrypted_res); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - - return Result::SendToServer; -} - -stdx::expected -AuthCachingSha2Forwarder::auth_data() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - auto src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::server::AuthMethodData>( - dst_channel, dst_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (msg_res->auth_method_data() == std::string_view("\x04")) { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("caching_sha2::forward::request_full_auth")); - } - - if (src_protocol->password().has_value()) { - discard_current_msg(dst_channel, dst_protocol); - - return send_password(); - } else { - stage(Stage::PlaintextPassword); - - return forward_server_to_client(); - } - } else if (msg_res->auth_method_data() == std::string_view("\x03")) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::fast_auth_ok")); - } - - // next is a Ok packet. - stage(Stage::Response); - - if (in_handshake_) { - // 0x03 means the client-greeting provided the right scrambled - // password that matches the cached entry. - - // as there is a password provided by the client already - // the client side expects either server::Ok or server::Error now. - // - // c<-r: server::greeting (from router) - // c->r: client::greeting (with tls handshake) - // c<-r: 0x01 0x04 - // c->r: password - // r->s: connect() - // r<-s: server::greeting - // r->s: client::greeting (with tls handshake, rehashed pwd) - // r<-s: 0x01 0x03 // current message - // r<-s: server::Ok - // c<-r: server::Ok - discard_current_msg(dst_channel, dst_protocol); - - // skip this message. - return Result::Again; - } else { - return forward_server_to_client(true); - } - } else { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::??\n" + - hexify(msg_res->auth_method_data()))); - } - stage(Stage::Response); - - return forward_server_to_client(); - } -} - -stdx::expected -AuthCachingSha2Forwarder::ok() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::ok")); - } - - // leave the message in the queue for the AuthForwarder. - return Result::Again; -} - -stdx::expected -AuthCachingSha2Forwarder::error() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("caching_sha2::forward::error")); - } - - // leave the message in the queue for the AuthForwarder. - return Result::Again; -} diff --git a/router/src/routing/src/classic_auth_caching_sha2.h b/router/src/routing/src/classic_auth_caching_sha2.h index c000215df550..f913b57ceff6 100644 --- a/router/src/routing/src/classic_auth_caching_sha2.h +++ b/router/src/routing/src/classic_auth_caching_sha2.h @@ -32,7 +32,6 @@ #include "classic_auth.h" #include "classic_connection_base.h" -#include "forwarding_processor.h" #include "mysql/harness/stdx/expected.h" // low-level routings for caching_sha2_password @@ -72,106 +71,4 @@ class AuthCachingSha2Password : public AuthBase { static bool is_public_key(const std::string_view &data); }; -class AuthCachingSha2Sender : public Processor { - public: - AuthCachingSha2Sender(MysqlRoutingClassicConnectionBase *conn, - std::string initial_server_auth_data, - std::string password) - : Processor(conn), - initial_server_auth_data_{std::move(initial_server_auth_data)}, - password_{std::move(password)} {} - - enum class Stage { - Init, - - PublicKey, - - Response, - - AuthData, - Error, - Ok, - - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - using Auth = AuthCachingSha2Password; - - stdx::expected init(); - stdx::expected public_key(); - stdx::expected response(); - stdx::expected auth_data(); - stdx::expected error(); - stdx::expected ok(); - - stdx::expected send_password(); - - Stage stage_{Stage::Init}; - - std::string initial_server_auth_data_; - std::string password_; -}; - -class AuthCachingSha2Forwarder : public ForwardingProcessor { - public: - AuthCachingSha2Forwarder(MysqlRoutingClassicConnectionBase *conn, - std::string initial_server_auth_data, - bool in_handshake = false) - : ForwardingProcessor(conn), - initial_server_auth_data_{std::move(initial_server_auth_data)}, - in_handshake_{in_handshake}, - stage_{in_handshake ? Stage::Response : Stage::Init} {} - - enum class Stage { - Init, - - ClientData, - EncryptedPassword, - PlaintextPassword, - - PublicKeyResponse, - PublicKey, - AuthData, - - Response, - - Error, - Ok, - - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - using Auth = AuthCachingSha2Password; - - stdx::expected init(); - stdx::expected client_data(); - stdx::expected encrypted_password(); - stdx::expected plaintext_password(); - stdx::expected auth_data(); - stdx::expected public_key_response(); - stdx::expected public_key(); - stdx::expected response(); - stdx::expected error(); - stdx::expected ok(); - - stdx::expected send_password(); - - std::string initial_server_auth_data_; - - bool in_handshake_; - Stage stage_; -}; - #endif diff --git a/router/src/routing/src/classic_auth_caching_sha2_forwarder.cc b/router/src/routing/src/classic_auth_caching_sha2_forwarder.cc new file mode 100644 index 000000000000..483635c49d3f --- /dev/null +++ b/router/src/routing/src/classic_auth_caching_sha2_forwarder.cc @@ -0,0 +1,579 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_auth_caching_sha2_forwarder.h" + +#include "auth_digest.h" +#include "classic_frame.h" +#include "harness_assert.h" +#include "hexify.h" +#include "mysql/harness/logging/logging.h" +#include "mysql/harness/stdx/expected.h" +#include "mysqld_error.h" // mysql-server error-codes +#include "mysqlrouter/classic_protocol_wire.h" + +IMPORT_LOG_FUNCTIONS() + +using mysql_harness::hexify; + +// Forwarder + +stdx::expected +AuthCachingSha2Forwarder::process() { + switch (stage()) { + case Stage::Init: + return init(); + case Stage::ClientData: + return client_data(); + case Stage::EncryptedPassword: + return encrypted_password(); + case Stage::PlaintextPassword: + return plaintext_password(); + case Stage::Response: + return response(); + case Stage::PublicKeyResponse: + return public_key_response(); + case Stage::PublicKey: + return public_key(); + case Stage::AuthData: + return auth_data(); + case Stage::Error: + return error(); + case Stage::Ok: + return ok(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected +AuthCachingSha2Forwarder::init() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->client_channel(); + auto dst_protocol = connection()->client_protocol(); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::switch")); + } + + auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::AuthMethodSwitch>( + dst_channel, dst_protocol, {Auth::kName, initial_server_auth_data_}); + if (!send_res) return send_client_failed(send_res.error()); + + stage(Stage::ClientData); + return Result::SendToClient; +} + +stdx::expected +AuthCachingSha2Forwarder::client_data() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->client_channel(); + auto src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::client::AuthMethodData>( + src_channel, src_protocol); + if (!msg_res) return recv_client_failed(msg_res.error()); + + if (Auth::is_public_key_request(msg_res->auth_method_data())) { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::forward::public_key_request")); + } + + if (AuthBase::connection_has_public_key(connection())) { + // send the router's public-key to be able to decrypt the client's + // password. + discard_current_msg(src_channel, src_protocol); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::public_key")); + } + + auto pubkey_res = Auth::public_key_from_ssl_ctx_as_pem( + connection()->context().source_ssl_ctx()->get()); + if (!pubkey_res) { + auto ec = pubkey_res.error(); + + if (ec != std::errc::function_not_supported) { + return send_client_failed(ec); + } + + stage(Stage::Done); + + // couldn't get the public key, fail the auth. + auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::Error>( + src_channel, src_protocol, + {ER_ACCESS_DENIED_ERROR, "Access denied", "HY000"}); + if (!send_res) return send_client_failed(send_res.error()); + } else { + // send the router's public key to the client. + stage(Stage::EncryptedPassword); + + auto send_res = + Auth::send_public_key(src_channel, src_protocol, *pubkey_res); + if (!send_res) return send_client_failed(send_res.error()); + } + + return Result::SendToClient; + } else { + // client requested a public key, but router has no ssl-ctx + // (client-ssl-mode is DISABLED|PASSTHROUGH) + // + // If the server-connection is secure, the server will treat the + // public-key-request as an invalid password + // (as it isn't terminated by \0) + stage(Stage::PublicKeyResponse); + + return forward_client_to_server(); + } + } else { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::forward::scrambled_password")); + } + + // if it isn't a public-key request, it is a fast-auth. + stage(Stage::Response); + + return forward_client_to_server(); + } +} + +// encrypted password from client to server. +stdx::expected +AuthCachingSha2Forwarder::encrypted_password() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->client_channel(); + auto src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::client::AuthMethodData>( + src_channel, src_protocol); + if (!msg_res) return recv_client_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::encrypted")); + } + + if (AuthBase::connection_has_public_key(connection())) { + auto nonce = initial_server_auth_data_; + + // if there is a trailing zero, strip it. + if (nonce.size() == Auth::kNonceLength + 1 && + nonce[Auth::kNonceLength] == 0x00) { + nonce = nonce.substr(0, Auth::kNonceLength); + } + + auto recv_res = Auth::rsa_decrypt_password( + connection()->context().source_ssl_ctx()->get(), + msg_res->auth_method_data(), nonce); + if (!recv_res) return recv_client_failed(recv_res.error()); + + src_protocol->password(*recv_res); + + discard_current_msg(src_channel, src_protocol); + + return send_password(); + } else { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::encrypted")); + } + + stage(Stage::Response); + + return forward_client_to_server(); + } +} + +// plaintext password from client to server. +stdx::expected +AuthCachingSha2Forwarder::plaintext_password() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->client_channel(); + auto src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::client::AuthMethodData>( + src_channel, src_protocol); + if (!msg_res) return recv_client_failed(msg_res.error()); + + if (socket_splicer->client_conn().is_secure_transport()) { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::forward::plaintext_password")); + } + + // remove trailing null + src_protocol->password(std::string( + AuthBase::strip_trailing_null(msg_res->auth_method_data()))); + + discard_current_msg(src_channel, src_protocol); + + return send_password(); + } else if (Auth::is_public_key_request(msg_res->auth_method_data())) { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::forward::public_key_request")); + } + + if (AuthBase::connection_has_public_key(connection())) { + // send the router's public-key to be able to decrypt the client's + // password. + discard_current_msg(src_channel, src_protocol); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::public_key")); + } + + auto pubkey_res = Auth::public_key_from_ssl_ctx_as_pem( + connection()->context().source_ssl_ctx()->get()); + if (!pubkey_res) { + auto ec = pubkey_res.error(); + + if (ec != std::errc::function_not_supported) { + return send_client_failed(ec); + } + + stage(Stage::Done); + + // couldn't get the public key, fail the auth. + auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::Error>( + src_channel, src_protocol, + {ER_ACCESS_DENIED_ERROR, "Access denied", "HY000"}); + if (!send_res) return send_client_failed(send_res.error()); + } else { + // send the router's public key to the client. + stage(Stage::EncryptedPassword); + + auto send_res = + Auth::send_public_key(src_channel, src_protocol, *pubkey_res); + if (!send_res) return send_client_failed(send_res.error()); + } + + return Result::SendToClient; + } else { + // client requested a public key, but router has no ssl-ctx + // (client-ssl-mode is DISABLED|PASSTHROUGH) + // + // If the server-connection is encrypted, the server will treat the + // public-key-request as an invalid password + // (as it isn't terminated by \0) + stage(Stage::PublicKeyResponse); + + return forward_client_to_server(); + } + } else { + discard_current_msg(src_channel, src_protocol); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::bad_message")); + } + + return recv_client_failed(make_error_code(std::errc::bad_message)); + } +} + +// encrypted password from client to server. +stdx::expected +AuthCachingSha2Forwarder::send_password() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_protocol = connection()->client_protocol(); + + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + if (socket_splicer->server_conn().is_secure_transport()) { + // the server-side is secure: send plaintext password + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::forward::plaintext_password")); + } + + stage(Stage::Response); + + auto send_res = Auth::send_plaintext_password( + dst_channel, dst_protocol, src_protocol->password().value()); + if (!send_res) return send_server_failed(send_res.error()); + } else { + // the server is NOT secure: ask for the server's publickey + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::forward::public_key_request")); + } + + stage(Stage::PublicKeyResponse); + + auto send_res = Auth::send_public_key_request(dst_channel, dst_protocol); + if (!send_res) return send_server_failed(send_res.error()); + } + + return Result::SendToServer; +} + +stdx::expected +AuthCachingSha2Forwarder::response() { + // ERR|OK|EOF|other + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Ok = ClassicFrame::cmd_byte(), + Error = ClassicFrame::cmd_byte(), + AuthData = ClassicFrame::cmd_byte< + classic_protocol::message::server::AuthMethodData>(), + }; + + switch (Msg{msg_type}) { + case Msg::AuthData: + stage(Stage::AuthData); + return Result::Again; + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug("received unexpected message from server in caching-sha2-auth:\n%s", + hexify(recv_buf).c_str()); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +stdx::expected +AuthCachingSha2Forwarder::public_key_response() { + // ERR|OK|EOF|other + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + AuthData = ClassicFrame::cmd_byte< + classic_protocol::message::server::AuthMethodData>(), + Error = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::AuthData: + stage(Stage::PublicKey); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::forward::public_key_response")); + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug( + "received unexpected message from server in sha256-password-auth:\n%s", + hexify(recv_buf).c_str()); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +// receive a public-key from the server. +stdx::expected +AuthCachingSha2Forwarder::public_key() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + auto src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::server::AuthMethodData>( + dst_channel, dst_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::public-key")); + } + + if (!src_protocol->password().has_value()) { + // the client's password isn't known. + // + // Forward the server's public-key to the client + stage(Stage::EncryptedPassword); + + return forward_server_to_client(); + } + + // as the plaintext password is known, encrypt it with the server's + // public-key. + auto pubkey_res = Auth::public_key_from_pem(msg_res->auth_method_data()); + if (!pubkey_res) return recv_server_failed(pubkey_res.error()); + + discard_current_msg(dst_channel, dst_protocol); + + auto password = *src_protocol->password(); + + auto nonce = initial_server_auth_data_; + + // if there is a trailing zero, strip it. + if (nonce.size() == Auth::kNonceLength + 1 && + nonce[Auth::kNonceLength] == 0x00) { + nonce = nonce.substr(0, Auth::kNonceLength); + } + + auto encrypted_res = Auth::rsa_encrypt_password(*pubkey_res, password, nonce); + if (!encrypted_res) return send_server_failed(encrypted_res.error()); + + auto send_res = + Auth::send_encrypted_password(dst_channel, dst_protocol, *encrypted_res); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + + return Result::SendToServer; +} + +stdx::expected +AuthCachingSha2Forwarder::auth_data() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + auto src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::server::AuthMethodData>( + dst_channel, dst_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (msg_res->auth_method_data() == std::string_view("\x04")) { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::forward::request_full_auth")); + } + + if (src_protocol->password().has_value()) { + discard_current_msg(dst_channel, dst_protocol); + + return send_password(); + } else { + stage(Stage::PlaintextPassword); + + return forward_server_to_client(); + } + } else if (msg_res->auth_method_data() == std::string_view("\x03")) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::fast_auth_ok")); + } + + // next is a Ok packet. + stage(Stage::Response); + + if (in_handshake_) { + // 0x03 means the client-greeting provided the right scrambled + // password that matches the cached entry. + + // as there is a password provided by the client already + // the client side expects either server::Ok or server::Error now. + // + // c<-r: server::greeting (from router) + // c->r: client::greeting (with tls handshake) + // c<-r: 0x01 0x04 + // c->r: password + // r->s: connect() + // r<-s: server::greeting + // r->s: client::greeting (with tls handshake, rehashed pwd) + // r<-s: 0x01 0x03 // current message + // r<-s: server::Ok + // c<-r: server::Ok + discard_current_msg(dst_channel, dst_protocol); + + // skip this message. + return Result::Again; + } else { + return forward_server_to_client(true); + } + } else { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::??\n" + + hexify(msg_res->auth_method_data()))); + } + stage(Stage::Response); + + return forward_server_to_client(); + } +} + +stdx::expected +AuthCachingSha2Forwarder::ok() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::ok")); + } + + // leave the message in the queue for the AuthForwarder. + return Result::Again; +} + +stdx::expected +AuthCachingSha2Forwarder::error() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::forward::error")); + } + + // leave the message in the queue for the AuthForwarder. + return Result::Again; +} diff --git a/router/src/routing/src/classic_auth_caching_sha2_forwarder.h b/router/src/routing/src/classic_auth_caching_sha2_forwarder.h new file mode 100644 index 000000000000..ec2e7201c942 --- /dev/null +++ b/router/src/routing/src/classic_auth_caching_sha2_forwarder.h @@ -0,0 +1,94 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_AUTH_CACHING_SHA2_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_AUTH_CACHING_SHA2_FORWARDER_INCLUDED + +#include +#include +#include + +#include "classic_auth.h" +#include "classic_auth_caching_sha2.h" +#include "classic_connection_base.h" +#include "forwarding_processor.h" +#include "mysql/harness/stdx/expected.h" + +class AuthCachingSha2Forwarder : public ForwardingProcessor { + public: + AuthCachingSha2Forwarder(MysqlRoutingClassicConnectionBase *conn, + std::string initial_server_auth_data, + bool in_handshake = false) + : ForwardingProcessor(conn), + initial_server_auth_data_{std::move(initial_server_auth_data)}, + in_handshake_{in_handshake}, + stage_{in_handshake ? Stage::Response : Stage::Init} {} + + enum class Stage { + Init, + + ClientData, + EncryptedPassword, + PlaintextPassword, + + PublicKeyResponse, + PublicKey, + AuthData, + + Response, + + Error, + Ok, + + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + using Auth = AuthCachingSha2Password; + + stdx::expected init(); + stdx::expected client_data(); + stdx::expected encrypted_password(); + stdx::expected plaintext_password(); + stdx::expected auth_data(); + stdx::expected public_key_response(); + stdx::expected public_key(); + stdx::expected response(); + stdx::expected error(); + stdx::expected ok(); + + stdx::expected send_password(); + + std::string initial_server_auth_data_; + + bool in_handshake_; + Stage stage_; +}; + +#endif diff --git a/router/src/routing/src/classic_auth_caching_sha2_sender.cc b/router/src/routing/src/classic_auth_caching_sha2_sender.cc new file mode 100644 index 000000000000..8f3cfaed17f2 --- /dev/null +++ b/router/src/routing/src/classic_auth_caching_sha2_sender.cc @@ -0,0 +1,260 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_auth_caching_sha2_sender.h" + +#include "auth_digest.h" +#include "classic_frame.h" +#include "harness_assert.h" +#include "hexify.h" +#include "mysql/harness/logging/logging.h" +#include "mysql/harness/stdx/expected.h" +#include "mysqld_error.h" // mysql-server error-codes + +IMPORT_LOG_FUNCTIONS() + +using mysql_harness::hexify; + +// sender + +stdx::expected +AuthCachingSha2Sender::process() { + switch (stage()) { + case Stage::Init: + return init(); + case Stage::Response: + return response(); + case Stage::PublicKey: + return public_key(); + case Stage::AuthData: + return auth_data(); + case Stage::Error: + return error(); + case Stage::Ok: + return ok(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected +AuthCachingSha2Sender::init() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + auto scramble_res = Auth::scramble( + AuthBase::strip_trailing_null(initial_server_auth_data_), password_); + if (!scramble_res) { + return send_server_failed(make_error_code(std::errc::invalid_argument)); + } + + auto send_res = ClassicFrame::send_msg( + dst_channel, dst_protocol, + classic_protocol::borrowed::message::client::AuthMethodData{ + *scramble_res}); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + + return Result::SendToServer; +} + +stdx::expected +AuthCachingSha2Sender::send_password() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + if (socket_splicer->server_conn().is_secure_transport()) { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::sender::plaintext_password")); + } + auto send_res = ClassicFrame::send_msg( + dst_channel, dst_protocol, + classic_protocol::borrowed::message::client::AuthMethodData{password_ + + '\0'}); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + } else { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::sender::public_key_request")); + } + + auto send_res = Auth::send_public_key_request(dst_channel, dst_protocol); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::PublicKey); + } + + return Result::SendToServer; +} + +stdx::expected +AuthCachingSha2Sender::response() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Ok = ClassicFrame::cmd_byte(), + Error = ClassicFrame::cmd_byte(), + AuthData = ClassicFrame::cmd_byte< + classic_protocol::message::server::AuthMethodData>(), + }; + + switch (Msg{msg_type}) { + case Msg::AuthData: + stage(Stage::AuthData); + return Result::Again; + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug("received unexpected message from server in caching-sha2-auth:\n%s", + hexify(recv_buf).c_str()); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +stdx::expected +AuthCachingSha2Sender::public_key() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::server::AuthMethodData>( + dst_channel, dst_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + auto msg = *msg_res; + + auto pubkey_res = Auth::public_key_from_pem(msg.auth_method_data()); + if (!pubkey_res) return recv_server_failed(pubkey_res.error()); + + // discard _after_ msg. is used as msg borrows from the dst_channel's + // recv_buffer. + discard_current_msg(dst_channel, dst_protocol); + + auto nonce = initial_server_auth_data_; + + // if there is a trailing zero, strip it. + if (nonce.size() == Auth::kNonceLength + 1 && + nonce[Auth::kNonceLength] == 0x00) { + nonce = nonce.substr(0, Auth::kNonceLength); + } + + auto encrypted_res = + Auth::rsa_encrypt_password(*pubkey_res, password_, nonce); + if (!encrypted_res) return send_server_failed(encrypted_res.error()); + + auto send_res = + Auth::send_encrypted_password(dst_channel, dst_protocol, *encrypted_res); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + + return Result::SendToServer; +} + +stdx::expected +AuthCachingSha2Sender::auth_data() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::server::AuthMethodData>( + dst_channel, dst_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (msg_res->auth_method_data() == std::string_view("\x04")) { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("caching_sha2::sender::request_full_auth")); + } + + discard_current_msg(dst_channel, dst_protocol); + + return send_password(); + } else if (msg_res->auth_method_data() == std::string_view("\x03")) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::sender::fast_auth_ok")); + } + + // as the client did the slow path, it doesn't expect a fast-auth-ok. + discard_current_msg(dst_channel, dst_protocol); + + // next should be an Ok + stage(Stage::Response); + + return Result::Again; + } else { + return recv_server_failed(make_error_code(std::errc::bad_message)); + } +} + +stdx::expected AuthCachingSha2Sender::ok() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::sender::ok")); + } + + return Result::Again; +} + +stdx::expected +AuthCachingSha2Sender::error() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("caching_sha2::sender::error")); + } + + return Result::Again; +} diff --git a/router/src/routing/src/classic_auth_caching_sha2_sender.h b/router/src/routing/src/classic_auth_caching_sha2_sender.h new file mode 100644 index 000000000000..dedc0404d288 --- /dev/null +++ b/router/src/routing/src/classic_auth_caching_sha2_sender.h @@ -0,0 +1,83 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_AUTH_CACHING_SHA2_SENDER_INCLUDED +#define ROUTING_CLASSIC_AUTH_CACHING_SHA2_SENDER_INCLUDED + +#include +#include +#include + +#include "classic_auth.h" +#include "classic_auth_caching_sha2.h" +#include "classic_connection_base.h" +#include "mysql/harness/stdx/expected.h" + +class AuthCachingSha2Sender : public Processor { + public: + AuthCachingSha2Sender(MysqlRoutingClassicConnectionBase *conn, + std::string initial_server_auth_data, + std::string password) + : Processor(conn), + initial_server_auth_data_{std::move(initial_server_auth_data)}, + password_{std::move(password)} {} + + enum class Stage { + Init, + + PublicKey, + + Response, + + AuthData, + Error, + Ok, + + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + using Auth = AuthCachingSha2Password; + + stdx::expected init(); + stdx::expected public_key(); + stdx::expected response(); + stdx::expected auth_data(); + stdx::expected error(); + stdx::expected ok(); + + stdx::expected send_password(); + + Stage stage_{Stage::Init}; + + std::string initial_server_auth_data_; + std::string password_; +}; + +#endif diff --git a/router/src/routing/src/classic_auth_cleartext.cc b/router/src/routing/src/classic_auth_cleartext.cc index 4859e23301d3..f4682585ab3e 100644 --- a/router/src/routing/src/classic_auth_cleartext.cc +++ b/router/src/routing/src/classic_auth_cleartext.cc @@ -24,261 +24,17 @@ #include "classic_auth_cleartext.h" -#include -#include // unique_ptr -#include - -#include -#include -#include - -#include "auth_digest.h" -#include "classic_frame.h" -#include "harness_assert.h" -#include "hexify.h" -#include "mysql/harness/logging/logging.h" -#include "mysql/harness/stdx/expected.h" -#include "mysqld_error.h" // mysql-server error-codes -#include "mysqlrouter/classic_protocol_wire.h" -#include "openssl_version.h" - -IMPORT_LOG_FUNCTIONS() - -using mysql_harness::hexify; +#include +#include +#include // AuthCleartextPassword std::optional AuthCleartextPassword::scramble( [[maybe_unused]] std::string_view nonce, std::string_view pwd) { - std::string s(pwd); - - s.push_back('\0'); - - return s; -} - -stdx::expected -AuthCleartextSender::process() { - switch (stage()) { - case Stage::Init: - return init(); - case Stage::Response: - return response(); - case Stage::Error: - return error(); - case Stage::Ok: - return ok(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected AuthCleartextSender::init() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - auto send_res = ClassicFrame::send_msg( - dst_channel, dst_protocol, - classic_protocol::borrowed::message::client::AuthMethodData{password_}); - if (!send_res) return send_server_failed(send_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("cleartext::sender::password")); - } - - stage(Stage::Response); - - return Result::SendToServer; -} - -stdx::expected -AuthCleartextSender::response() { - // ERR|OK|EOF|other - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Ok = ClassicFrame::cmd_byte(), - Error = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug("received unexpected message from server in cleartext-auth:\n%s", - hexify(recv_buf).c_str()); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -stdx::expected AuthCleartextSender::ok() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("cleartext::sender::ok")); - } - - return Result::Again; -} - -stdx::expected -AuthCleartextSender::error() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("cleartext::sender::error")); - } - - return Result::Again; -} - -stdx::expected -AuthCleartextForwarder::process() { - switch (stage()) { - case Stage::Init: - return init(); - case Stage::ClientData: - return client_data(); - case Stage::Response: - return response(); - case Stage::Error: - return error(); - case Stage::Ok: - return ok(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected -AuthCleartextForwarder::init() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->client_channel(); - auto dst_protocol = connection()->client_protocol(); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("cleartext::forward::switch")); - } - - auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::AuthMethodSwitch>( - dst_channel, dst_protocol, {Auth::kName, initial_server_auth_data_}); - if (!send_res) return send_client_failed(send_res.error()); - - stage(Stage::ClientData); - - return Result::SendToClient; -} - -stdx::expected -AuthCleartextForwarder::client_data() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->client_channel(); - auto src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::client::AuthMethodData>( - src_channel, src_protocol); - if (!msg_res) return recv_client_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("cleartext::forward::plaintext_password")); - } - - stage(Stage::Response); - - return forward_client_to_server(); -} - -stdx::expected -AuthCleartextForwarder::response() { - // ERR|OK|EOF|other - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Ok = ClassicFrame::cmd_byte(), - Error = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("cleartext::forward::response")); - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug("received unexpected message from server in cleartext-auth:\n%s", - hexify(recv_buf).c_str()); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -stdx::expected -AuthCleartextForwarder::ok() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("cleartext::forward::ok")); - } - - // leave the message in the queue for the AuthForwarder. - return Result::Again; -} - -stdx::expected -AuthCleartextForwarder::error() { - stage(Stage::Done); + std::string str(pwd); - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("cleartext::forward::error")); - } + str.push_back('\0'); - // leave the message in the queue for the AuthForwarder. - return Result::Again; + return str; } diff --git a/router/src/routing/src/classic_auth_cleartext.h b/router/src/routing/src/classic_auth_cleartext.h index 94ce67c57bf6..4669534f00f2 100644 --- a/router/src/routing/src/classic_auth_cleartext.h +++ b/router/src/routing/src/classic_auth_cleartext.h @@ -25,17 +25,12 @@ #ifndef ROUTING_CLASSIC_AUTH_CLEARTEXT_INCLUDED #define ROUTING_CLASSIC_AUTH_CLEARTEXT_INCLUDED -#include // unique_ptr #include +#include #include #include -#include - -#include "classic_auth.h" #include "classic_connection_base.h" -#include "forwarding_processor.h" -#include "mysql/harness/stdx/expected.h" class AuthCleartextPassword { public: @@ -45,81 +40,4 @@ class AuthCleartextPassword { std::string_view pwd); }; -class AuthCleartextSender : public Processor { - public: - AuthCleartextSender(MysqlRoutingClassicConnectionBase *conn, - std::string initial_server_auth_data, - std::string password) - : Processor(conn), - initial_server_auth_data_{std::move(initial_server_auth_data)}, - password_{std::move(password)} {} - - enum class Stage { - Init, - - Response, - - Error, - Ok, - - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - stdx::expected init(); - stdx::expected response(); - stdx::expected error(); - stdx::expected ok(); - - Stage stage_{Stage::Init}; - - std::string initial_server_auth_data_; - std::string password_; -}; - -class AuthCleartextForwarder : public ForwardingProcessor { - public: - AuthCleartextForwarder(MysqlRoutingClassicConnectionBase *conn, - std::string initial_server_auth_data, - bool in_handshake = false) - : ForwardingProcessor(conn), - initial_server_auth_data_{std::move(initial_server_auth_data)}, - stage_{in_handshake ? Stage::Response : Stage::Init} {} - - enum class Stage { - Init, - - ClientData, - Response, - - Error, - Ok, - - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - using Auth = AuthCleartextPassword; - - stdx::expected init(); - stdx::expected client_data(); - stdx::expected response(); - stdx::expected error(); - stdx::expected ok(); - - std::string initial_server_auth_data_; - - Stage stage_{Stage::Init}; -}; - #endif diff --git a/router/src/routing/src/classic_auth_cleartext_forwarder.cc b/router/src/routing/src/classic_auth_cleartext_forwarder.cc new file mode 100644 index 000000000000..99bf7101aa05 --- /dev/null +++ b/router/src/routing/src/classic_auth_cleartext_forwarder.cc @@ -0,0 +1,175 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_auth_cleartext_forwarder.h" + +#include +#include // unique_ptr +#include + +#include +#include +#include + +#include "auth_digest.h" +#include "classic_frame.h" +#include "harness_assert.h" +#include "hexify.h" +#include "mysql/harness/logging/logging.h" +#include "mysql/harness/stdx/expected.h" +#include "mysqld_error.h" // mysql-server error-codes +#include "mysqlrouter/classic_protocol_wire.h" +#include "openssl_version.h" + +IMPORT_LOG_FUNCTIONS() + +using mysql_harness::hexify; + +stdx::expected +AuthCleartextForwarder::process() { + switch (stage()) { + case Stage::Init: + return init(); + case Stage::ClientData: + return client_data(); + case Stage::Response: + return response(); + case Stage::Error: + return error(); + case Stage::Ok: + return ok(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected +AuthCleartextForwarder::init() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->client_channel(); + auto dst_protocol = connection()->client_protocol(); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("cleartext::forward::switch")); + } + + auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::AuthMethodSwitch>( + dst_channel, dst_protocol, {Auth::kName, initial_server_auth_data_}); + if (!send_res) return send_client_failed(send_res.error()); + + stage(Stage::ClientData); + + return Result::SendToClient; +} + +stdx::expected +AuthCleartextForwarder::client_data() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->client_channel(); + auto src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::client::AuthMethodData>( + src_channel, src_protocol); + if (!msg_res) return recv_client_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("cleartext::forward::plaintext_password")); + } + + stage(Stage::Response); + + return forward_client_to_server(); +} + +stdx::expected +AuthCleartextForwarder::response() { + // ERR|OK|EOF|other + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Ok = ClassicFrame::cmd_byte(), + Error = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("cleartext::forward::response")); + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug("received unexpected message from server in cleartext-auth:\n%s", + hexify(recv_buf).c_str()); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +stdx::expected +AuthCleartextForwarder::ok() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("cleartext::forward::ok")); + } + + // leave the message in the queue for the AuthForwarder. + return Result::Again; +} + +stdx::expected +AuthCleartextForwarder::error() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("cleartext::forward::error")); + } + + // leave the message in the queue for the AuthForwarder. + return Result::Again; +} diff --git a/router/src/routing/src/classic_auth_cleartext_forwarder.h b/router/src/routing/src/classic_auth_cleartext_forwarder.h new file mode 100644 index 000000000000..82f318b5592d --- /dev/null +++ b/router/src/routing/src/classic_auth_cleartext_forwarder.h @@ -0,0 +1,78 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_AUTH_CLEARTEXT_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_AUTH_CLEARTEXT_FORWARDER_INCLUDED + +#include +#include +#include + +#include "classic_auth.h" +#include "classic_auth_cleartext.h" +#include "classic_connection_base.h" +#include "forwarding_processor.h" +#include "mysql/harness/stdx/expected.h" + +class AuthCleartextForwarder : public ForwardingProcessor { + public: + AuthCleartextForwarder(MysqlRoutingClassicConnectionBase *conn, + std::string initial_server_auth_data, + bool in_handshake = false) + : ForwardingProcessor(conn), + initial_server_auth_data_{std::move(initial_server_auth_data)}, + stage_{in_handshake ? Stage::Response : Stage::Init} {} + + enum class Stage { + Init, + + ClientData, + Response, + + Error, + Ok, + + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + using Auth = AuthCleartextPassword; + + stdx::expected init(); + stdx::expected client_data(); + stdx::expected response(); + stdx::expected error(); + stdx::expected ok(); + + std::string initial_server_auth_data_; + + Stage stage_{Stage::Init}; +}; + +#endif diff --git a/router/src/routing/src/classic_auth_cleartext_sender.cc b/router/src/routing/src/classic_auth_cleartext_sender.cc new file mode 100644 index 000000000000..31f050e4eef4 --- /dev/null +++ b/router/src/routing/src/classic_auth_cleartext_sender.cc @@ -0,0 +1,137 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_auth_cleartext_sender.h" + +#include + +#include "auth_digest.h" +#include "classic_frame.h" +#include "harness_assert.h" +#include "hexify.h" +#include "mysql/harness/logging/logging.h" +#include "mysql/harness/stdx/expected.h" +#include "mysqld_error.h" // mysql-server error-codes + +IMPORT_LOG_FUNCTIONS() + +using mysql_harness::hexify; + +stdx::expected +AuthCleartextSender::process() { + switch (stage()) { + case Stage::Init: + return init(); + case Stage::Response: + return response(); + case Stage::Error: + return error(); + case Stage::Ok: + return ok(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected AuthCleartextSender::init() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + auto send_res = ClassicFrame::send_msg( + dst_channel, dst_protocol, + classic_protocol::borrowed::message::client::AuthMethodData{password_}); + if (!send_res) return send_server_failed(send_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("cleartext::sender::password")); + } + + stage(Stage::Response); + + return Result::SendToServer; +} + +stdx::expected +AuthCleartextSender::response() { + // ERR|OK|EOF|other + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Ok = ClassicFrame::cmd_byte(), + Error = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug("received unexpected message from server in cleartext-auth:\n%s", + hexify(recv_buf).c_str()); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +stdx::expected AuthCleartextSender::ok() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("cleartext::sender::ok")); + } + + return Result::Again; +} + +stdx::expected +AuthCleartextSender::error() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("cleartext::sender::error")); + } + + return Result::Again; +} diff --git a/router/src/routing/src/classic_auth_cleartext_sender.h b/router/src/routing/src/classic_auth_cleartext_sender.h new file mode 100644 index 000000000000..70230bd9c013 --- /dev/null +++ b/router/src/routing/src/classic_auth_cleartext_sender.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_AUTH_CLEARTEXT_SENDER_INCLUDED +#define ROUTING_CLASSIC_AUTH_CLEARTEXT_SENDER_INCLUDED + +#include +#include +#include + +#include "classic_auth.h" +#include "classic_auth_cleartext.h" +#include "classic_connection_base.h" +#include "mysql/harness/stdx/expected.h" +#include "processor.h" + +class AuthCleartextSender : public Processor { + public: + AuthCleartextSender(MysqlRoutingClassicConnectionBase *conn, + std::string initial_server_auth_data, + std::string password) + : Processor(conn), + initial_server_auth_data_{std::move(initial_server_auth_data)}, + password_{std::move(password)} {} + + enum class Stage { + Init, + + Response, + + Error, + Ok, + + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + stdx::expected init(); + stdx::expected response(); + stdx::expected error(); + stdx::expected ok(); + + Stage stage_{Stage::Init}; + + std::string initial_server_auth_data_; + std::string password_; +}; + +#endif diff --git a/router/src/routing/src/classic_auth_forwarder.cc b/router/src/routing/src/classic_auth_forwarder.cc index a2969106ea01..2269529989c1 100644 --- a/router/src/routing/src/classic_auth_forwarder.cc +++ b/router/src/routing/src/classic_auth_forwarder.cc @@ -32,10 +32,14 @@ #include #include "auth_digest.h" -#include "classic_auth_caching_sha2.h" -#include "classic_auth_cleartext.h" -#include "classic_auth_native.h" -#include "classic_auth_sha256_password.h" +#include "classic_auth_caching_sha2_forwarder.h" +#include "classic_auth_caching_sha2_sender.h" +#include "classic_auth_cleartext_forwarder.h" +#include "classic_auth_cleartext_sender.h" +#include "classic_auth_native_forwarder.h" +#include "classic_auth_native_sender.h" +#include "classic_auth_sha256_password_forwarder.h" +#include "classic_auth_sha256_password_sender.h" #include "classic_frame.h" #include "harness_assert.h" #include "hexify.h" diff --git a/router/src/routing/src/classic_auth_native.cc b/router/src/routing/src/classic_auth_native.cc index ed507c7bc53b..fdc6d20fa02e 100644 --- a/router/src/routing/src/classic_auth_native.cc +++ b/router/src/routing/src/classic_auth_native.cc @@ -26,13 +26,6 @@ #include "auth_digest.h" #include "classic_auth.h" -#include "classic_frame.h" -#include "hexify.h" -#include "mysql/harness/logging/logging.h" - -IMPORT_LOG_FUNCTIONS() - -using mysql_harness::hexify; // AuthNativePassword @@ -40,230 +33,3 @@ std::optional AuthNativePassword::scramble(std::string_view nonce, std::string_view pwd) { return mysql_native_password_scramble(nonce, pwd); } - -stdx::expected AuthNativeSender::process() { - switch (stage()) { - case Stage::Init: - return init(); - case Stage::Response: - return response(); - case Stage::Error: - return error(); - case Stage::Ok: - return ok(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected AuthNativeSender::init() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - auto scramble_res = Auth::scramble( - AuthBase::strip_trailing_null(initial_server_auth_data_), password_); - if (!scramble_res) { - return send_server_failed(make_error_code(std::errc::invalid_argument)); - } - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("native::sender::scrambled_password")); - } - - auto send_res = ClassicFrame::send_msg( - dst_channel, dst_protocol, - classic_protocol::borrowed::message::client::AuthMethodData{ - *scramble_res}); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - - return Result::SendToServer; -} - -stdx::expected -AuthNativeSender::response() { - // ERR|OK|EOF|other - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Ok = ClassicFrame::cmd_byte(), - Error = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("native::sender::response")); - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug("received unexpected message from server in native-auth:\n%s", - hexify(recv_buf).c_str()); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -stdx::expected AuthNativeSender::ok() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("native::sender::ok")); - } - - return Result::Again; -} - -stdx::expected AuthNativeSender::error() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("native::sender::error")); - } - - return Result::Again; -} - -stdx::expected -AuthNativeForwarder::process() { - switch (stage()) { - case Stage::Init: - return init(); - case Stage::ClientData: - return client_data(); - case Stage::Response: - return response(); - case Stage::Error: - return error(); - case Stage::Ok: - return ok(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -// server switched to mysql_native_password -stdx::expected AuthNativeForwarder::init() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->client_channel(); - auto dst_protocol = connection()->client_protocol(); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("native::forward::switch")); - } - - auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::AuthMethodSwitch>( - dst_channel, dst_protocol, {Auth::kName, initial_server_auth_data_}); - if (!send_res) return send_client_failed(send_res.error()); - - stage(Stage::ClientData); - return Result::SendToClient; -} - -stdx::expected -AuthNativeForwarder::client_data() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->client_channel(); - auto src_protocol = connection()->client_protocol(); - - auto read_res = ClassicFrame::ensure_frame_header(src_channel, src_protocol); - if (!read_res) return recv_client_failed(read_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("native::forward::scrambled_password")); - } - - stage(Stage::Response); - - return forward_client_to_server(); -} - -stdx::expected -AuthNativeForwarder::response() { - // ERR|OK|EOF|other - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Ok = ClassicFrame::cmd_byte(), - Error = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug("received unexpected message from server in native-auth:\n%s", - hexify(recv_buf).c_str()); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -stdx::expected AuthNativeForwarder::ok() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("native::forward::ok")); - } - - // leave the message in the queue for the AuthForwarder. - return Result::Again; -} - -stdx::expected -AuthNativeForwarder::error() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("native::forward::error")); - } - - // leave the message in the queue for the AuthForwarder. - return Result::Again; -} diff --git a/router/src/routing/src/classic_auth_native.h b/router/src/routing/src/classic_auth_native.h index 2e489519640b..64cee0781762 100644 --- a/router/src/routing/src/classic_auth_native.h +++ b/router/src/routing/src/classic_auth_native.h @@ -28,11 +28,6 @@ #include #include #include -#include - -#include "classic_connection_base.h" -#include "forwarding_processor.h" -#include "mysql/harness/stdx/expected.h" class AuthNativePassword { public: @@ -42,82 +37,4 @@ class AuthNativePassword { std::string_view pwd); }; -class AuthNativeSender : public Processor { - public: - AuthNativeSender(MysqlRoutingClassicConnectionBase *conn, - std::string initial_server_auth_data, std::string password) - : Processor(conn), - initial_server_auth_data_{std::move(initial_server_auth_data)}, - password_{std::move(password)} {} - - enum class Stage { - Init, - - Response, - - Error, - Ok, - - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - using Auth = AuthNativePassword; - - stdx::expected init(); - stdx::expected response(); - stdx::expected error(); - stdx::expected ok(); - - Stage stage_{Stage::Init}; - - std::string initial_server_auth_data_; - std::string password_; -}; - -class AuthNativeForwarder : public ForwardingProcessor { - public: - AuthNativeForwarder(MysqlRoutingClassicConnectionBase *conn, - std::string initial_server_auth_data, - bool in_handshake = false) - : ForwardingProcessor(conn), - initial_server_auth_data_{std::move(initial_server_auth_data)}, - stage_{in_handshake ? Stage::Response : Stage::Init} {} - - enum class Stage { - Init, - - ClientData, - Response, - - Error, - Ok, - - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - using Auth = AuthNativePassword; - - stdx::expected init(); - stdx::expected client_data(); - stdx::expected response(); - stdx::expected error(); - stdx::expected ok(); - - std::string initial_server_auth_data_; - - Stage stage_; -}; - #endif diff --git a/router/src/routing/src/classic_auth_native_forwarder.cc b/router/src/routing/src/classic_auth_native_forwarder.cc new file mode 100644 index 000000000000..1129a60c9b75 --- /dev/null +++ b/router/src/routing/src/classic_auth_native_forwarder.cc @@ -0,0 +1,154 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_auth_native_forwarder.h" + +#include "classic_auth.h" +#include "classic_frame.h" +#include "hexify.h" +#include "mysql/harness/logging/logging.h" + +IMPORT_LOG_FUNCTIONS() + +using mysql_harness::hexify; + +stdx::expected +AuthNativeForwarder::process() { + switch (stage()) { + case Stage::Init: + return init(); + case Stage::ClientData: + return client_data(); + case Stage::Response: + return response(); + case Stage::Error: + return error(); + case Stage::Ok: + return ok(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +// server switched to mysql_native_password +stdx::expected AuthNativeForwarder::init() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->client_channel(); + auto dst_protocol = connection()->client_protocol(); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("native::forward::switch")); + } + + auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::AuthMethodSwitch>( + dst_channel, dst_protocol, {Auth::kName, initial_server_auth_data_}); + if (!send_res) return send_client_failed(send_res.error()); + + stage(Stage::ClientData); + return Result::SendToClient; +} + +stdx::expected +AuthNativeForwarder::client_data() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->client_channel(); + auto src_protocol = connection()->client_protocol(); + + auto read_res = ClassicFrame::ensure_frame_header(src_channel, src_protocol); + if (!read_res) return recv_client_failed(read_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("native::forward::scrambled_password")); + } + + stage(Stage::Response); + + return forward_client_to_server(); +} + +stdx::expected +AuthNativeForwarder::response() { + // ERR|OK|EOF|other + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Ok = ClassicFrame::cmd_byte(), + Error = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug("received unexpected message from server in native-auth:\n%s", + hexify(recv_buf).c_str()); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +stdx::expected AuthNativeForwarder::ok() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("native::forward::ok")); + } + + // leave the message in the queue for the AuthForwarder. + return Result::Again; +} + +stdx::expected +AuthNativeForwarder::error() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("native::forward::error")); + } + + // leave the message in the queue for the AuthForwarder. + return Result::Again; +} diff --git a/router/src/routing/src/classic_auth_native_forwarder.h b/router/src/routing/src/classic_auth_native_forwarder.h new file mode 100644 index 000000000000..0f455e3025bf --- /dev/null +++ b/router/src/routing/src/classic_auth_native_forwarder.h @@ -0,0 +1,78 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_AUTH_NATIVE_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_AUTH_NATIVE_FORWARDER_INCLUDED + +#include +#include +#include +#include + +#include "classic_auth_native.h" +#include "classic_connection_base.h" +#include "forwarding_processor.h" +#include "mysql/harness/stdx/expected.h" + +class AuthNativeForwarder : public ForwardingProcessor { + public: + AuthNativeForwarder(MysqlRoutingClassicConnectionBase *conn, + std::string initial_server_auth_data, + bool in_handshake = false) + : ForwardingProcessor(conn), + initial_server_auth_data_{std::move(initial_server_auth_data)}, + stage_{in_handshake ? Stage::Response : Stage::Init} {} + + enum class Stage { + Init, + + ClientData, + Response, + + Error, + Ok, + + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + using Auth = AuthNativePassword; + + stdx::expected init(); + stdx::expected client_data(); + stdx::expected response(); + stdx::expected error(); + stdx::expected ok(); + + std::string initial_server_auth_data_; + + Stage stage_; +}; + +#endif diff --git a/router/src/routing/src/classic_auth_native_sender.cc b/router/src/routing/src/classic_auth_native_sender.cc new file mode 100644 index 000000000000..06502f2e580d --- /dev/null +++ b/router/src/routing/src/classic_auth_native_sender.cc @@ -0,0 +1,142 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_auth_native_sender.h" + +#include "auth_digest.h" +#include "classic_auth.h" +#include "classic_frame.h" +#include "hexify.h" +#include "mysql/harness/logging/logging.h" + +IMPORT_LOG_FUNCTIONS() + +using mysql_harness::hexify; + +stdx::expected AuthNativeSender::process() { + switch (stage()) { + case Stage::Init: + return init(); + case Stage::Response: + return response(); + case Stage::Error: + return error(); + case Stage::Ok: + return ok(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected AuthNativeSender::init() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + auto scramble_res = Auth::scramble( + AuthBase::strip_trailing_null(initial_server_auth_data_), password_); + if (!scramble_res) { + return send_server_failed(make_error_code(std::errc::invalid_argument)); + } + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("native::sender::scrambled_password")); + } + + auto send_res = ClassicFrame::send_msg( + dst_channel, dst_protocol, + classic_protocol::borrowed::message::client::AuthMethodData{ + *scramble_res}); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + + return Result::SendToServer; +} + +stdx::expected +AuthNativeSender::response() { + // ERR|OK|EOF|other + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Ok = ClassicFrame::cmd_byte(), + Error = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("native::sender::response")); + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug("received unexpected message from server in native-auth:\n%s", + hexify(recv_buf).c_str()); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +stdx::expected AuthNativeSender::ok() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("native::sender::ok")); + } + + return Result::Again; +} + +stdx::expected AuthNativeSender::error() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("native::sender::error")); + } + + return Result::Again; +} diff --git a/router/src/routing/src/classic_auth_native_sender.h b/router/src/routing/src/classic_auth_native_sender.h new file mode 100644 index 000000000000..07b9f17d747f --- /dev/null +++ b/router/src/routing/src/classic_auth_native_sender.h @@ -0,0 +1,76 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_AUTH_NATIVE_SENDER_INCLUDED +#define ROUTING_CLASSIC_AUTH_NATIVE_SENDER_INCLUDED + +#include +#include +#include +#include + +#include "classic_auth_native.h" +#include "classic_connection_base.h" +#include "mysql/harness/stdx/expected.h" +#include "processor.h" + +class AuthNativeSender : public Processor { + public: + AuthNativeSender(MysqlRoutingClassicConnectionBase *conn, + std::string initial_server_auth_data, std::string password) + : Processor(conn), + initial_server_auth_data_{std::move(initial_server_auth_data)}, + password_{std::move(password)} {} + + enum class Stage { + Init, + + Response, + + Error, + Ok, + + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + using Auth = AuthNativePassword; + + stdx::expected init(); + stdx::expected response(); + stdx::expected error(); + stdx::expected ok(); + + Stage stage_{Stage::Init}; + + std::string initial_server_auth_data_; + std::string password_; +}; + +#endif diff --git a/router/src/routing/src/classic_auth_sha256_password.cc b/router/src/routing/src/classic_auth_sha256_password.cc index 491b00c89e1a..2e031326e0a1 100644 --- a/router/src/routing/src/classic_auth_sha256_password.cc +++ b/router/src/routing/src/classic_auth_sha256_password.cc @@ -24,15 +24,7 @@ #include "classic_auth_sha256_password.h" -#include "auth_digest.h" #include "classic_frame.h" -#include "hexify.h" -#include "mysql/harness/logging/logging.h" -#include "mysqld_error.h" // mysql-server error-codes - -IMPORT_LOG_FUNCTIONS() - -using mysql_harness::hexify; // AuthSha256Password @@ -88,558 +80,3 @@ bool AuthSha256Password::is_public_key_request(const std::string_view &data) { bool AuthSha256Password::is_public_key(const std::string_view &data) { return data.size() == 256; } - -// sender - -stdx::expected AuthSha256Sender::process() { - switch (stage()) { - case Stage::Init: - return init(); - case Stage::Response: - return response(); - case Stage::PublicKey: - return public_key(); - case Stage::Error: - return error(); - case Stage::Ok: - return ok(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected AuthSha256Sender::init() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - if (dst_channel->ssl() || password_.empty()) { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("sha256_password::sender::plaintext_password")); - } - - auto send_res = ClassicFrame::send_msg( - dst_channel, dst_protocol, - classic_protocol::borrowed::message::client::AuthMethodData{password_ + - '\0'}); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - } else { - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("sha256_password::sender::public_key_request")); - } - auto send_res = Auth::send_public_key_request(dst_channel, dst_protocol); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::PublicKey); - } - - return Result::SendToServer; -} - -stdx::expected -AuthSha256Sender::response() { - // ERR|OK|EOF|other - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Ok = ClassicFrame::cmd_byte(), - Error = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug("received unexpected message from server in sha256-auth:\n%s", - mysql_harness::hexify(recv_buf).c_str()); - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -stdx::expected -AuthSha256Sender::public_key() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::server::AuthMethodData>( - dst_channel, dst_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - auto msg = *msg_res; - - auto pubkey_res = Auth::public_key_from_pem(msg.auth_method_data()); - if (!pubkey_res) return recv_server_failed(pubkey_res.error()); - - // discard _after_ msg. is used as msg borrows from the dst_channel's - // recv_buffer. - discard_current_msg(dst_channel, dst_protocol); - - auto nonce = initial_server_auth_data_; - - // if there is a trailing zero, strip it. - if (nonce.size() == Auth::kNonceLength + 1 && - nonce[Auth::kNonceLength] == 0x00) { - nonce = nonce.substr(0, Auth::kNonceLength); - } - - auto encrypted_res = - Auth::rsa_encrypt_password(*pubkey_res, password_, nonce); - if (!encrypted_res) return send_server_failed(encrypted_res.error()); - - auto send_res = - Auth::send_encrypted_password(dst_channel, dst_protocol, *encrypted_res); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - - return Result::SendToServer; -} - -stdx::expected AuthSha256Sender::ok() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::sender::ok")); - } - - return Result::Again; -} - -stdx::expected AuthSha256Sender::error() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::sender::error")); - } - - return Result::Again; -} - -// forwarder - -stdx::expected -AuthSha256Forwarder::process() { - switch (stage()) { - case Stage::Init: - return init(); - case Stage::ClientData: - return client_data(); - case Stage::EncryptedPassword: - return encrypted_password(); - case Stage::Response: - return response(); - case Stage::PublicKeyResponse: - return public_key_response(); - case Stage::PublicKey: - return public_key(); - case Stage::Error: - return error(); - case Stage::Ok: - return ok(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected AuthSha256Forwarder::init() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->client_channel(); - auto dst_protocol = connection()->client_protocol(); - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::switch")); - } - - auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::AuthMethodSwitch>( - dst_channel, dst_protocol, {Auth::kName, initial_server_auth_data_}); - if (!send_res) return send_client_failed(send_res.error()); - - stage(Stage::ClientData); - - return Result::SendToClient; -} - -stdx::expected -AuthSha256Forwarder::client_data() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->client_channel(); - auto src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::client::AuthMethodData>( - src_channel, src_protocol); - if (!msg_res) return recv_client_failed(msg_res.error()); - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::client_data:\n" + - hexify(msg_res->auth_method_data()))); - } - - if (src_channel->ssl() || - msg_res->auth_method_data() == Auth::kEmptyPassword) { - // password is null-terminated, remove it. - src_protocol->password(std::string( - AuthBase::strip_trailing_null(msg_res->auth_method_data()))); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::password:\n" + - hexify(*src_protocol->password()))); - } - - discard_current_msg(src_channel, src_protocol); - - return send_password(); - } else if (Auth::is_public_key_request(msg_res->auth_method_data())) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage( - "sha256_password::forward::public_key_request")); - } - - if (AuthBase::connection_has_public_key(connection())) { - // send the router's public-key to be able to decrypt the client's - // password. - discard_current_msg(src_channel, src_protocol); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::public_key")); - } - - auto pubkey_res = Auth::public_key_from_ssl_ctx_as_pem( - connection()->context().source_ssl_ctx()->get()); - if (!pubkey_res) { - auto ec = pubkey_res.error(); - - if (ec != std::errc::function_not_supported) { - return send_client_failed(ec); - } - - stage(Stage::Done); - - // couldn't get the public key, fail the auth. - auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::Error>( - src_channel, src_protocol, - {ER_ACCESS_DENIED_ERROR, "Access denied", "HY000"}); - if (!send_res) return send_client_failed(send_res.error()); - } else { - // send the router's public key to the client. - stage(Stage::EncryptedPassword); - - auto send_res = - Auth::send_public_key(src_channel, src_protocol, *pubkey_res); - if (!send_res) return send_client_failed(send_res.error()); - } - - return Result::SendToClient; - } else { - // client requested a public key, but router has no ssl-ctx - // (client-ssl-mode is DISABLED|PASSTHROUGH) - // - // If the server-connection is encrypted, the server will treat the - // public-key-request as an invalid password - // (as it isn't terminated by \0) - stage(Stage::PublicKeyResponse); - - return forward_client_to_server(); - } - } else { - discard_current_msg(src_channel, src_protocol); - - Tracer::Event().stage("sha256_password::forward::bad_message:\n" + - hexify(msg_res->auth_method_data())); - - return recv_client_failed(make_error_code(std::errc::bad_message)); - } -} - -// encrypted password from client to server. -stdx::expected -AuthSha256Forwarder::encrypted_password() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->client_channel(); - auto src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::client::AuthMethodData>( - src_channel, src_protocol); - if (!msg_res) return recv_client_failed(msg_res.error()); - - if (AuthBase::connection_has_public_key(connection())) { - auto nonce = src_protocol->auth_method_data(); - - // if there is a trailing zero, strip it. - if (nonce.size() == Auth::kNonceLength + 1 && - nonce[Auth::kNonceLength] == 0x00) { - nonce = nonce.substr(0, Auth::kNonceLength); - } - - auto recv_res = Auth::rsa_decrypt_password( - connection()->context().source_ssl_ctx()->get(), - msg_res->auth_method_data(), nonce); - if (!recv_res) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::decrypt:\n" + - recv_res.error().message())); - } - return recv_client_failed(recv_res.error()); - } - - src_protocol->password(*recv_res); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::password:\n" + - hexify(*src_protocol->password()))); - } - - discard_current_msg(src_channel, src_protocol); - - return send_password(); - } else { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::encrypted")); - } - - stage(Stage::Response); - - return forward_client_to_server(); - } -} - -// encrypted password from client to server. -stdx::expected -AuthSha256Forwarder::send_password() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_protocol = connection()->client_protocol(); - - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - if (dst_channel->ssl() || src_protocol->password()->empty()) { - // the server-side is encrypted (or the password is empty): - // - // send plaintext password - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage( - "sha256_password::forward::plaintext_password")); - } - - stage(Stage::Response); - - auto send_res = Auth::send_plaintext_password( - dst_channel, dst_protocol, src_protocol->password().value()); - if (!send_res) return send_server_failed(send_res.error()); - } else { - // the server is NOT encrypted: ask for the server's publickey - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage( - "sha256_password::forward::public_key_request")); - } - - stage(Stage::PublicKeyResponse); - - auto send_res = Auth::send_public_key_request(dst_channel, dst_protocol); - if (!send_res) return send_server_failed(send_res.error()); - } - - return Result::SendToServer; -} - -stdx::expected -AuthSha256Forwarder::response() { - // ERR|OK|EOF|other - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Ok = ClassicFrame::cmd_byte(), - Error = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::response")); - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug( - "received unexpected message from server in sha256-password-auth:\n%s", - hexify(recv_buf).c_str()); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -stdx::expected -AuthSha256Forwarder::public_key_response() { - // ERR|OK|EOF|other - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // ensure the recv_buf has at last frame-header (+ msg-byte) - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - AuthData = ClassicFrame::cmd_byte< - classic_protocol::message::server::AuthMethodData>(), - Error = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::AuthData: - stage(Stage::PublicKey); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::response")); - } - - // if there is another packet, dump its payload for now. - auto &recv_buf = src_channel->recv_plain_view(); - - // get as much data of the current frame from the recv-buffers to log it. - (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - - log_debug( - "received unexpected message from server in sha256-password-auth:\n%s", - hexify(recv_buf).c_str()); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -/* - * @pre - * a public-key request was sent to the server. - */ -stdx::expected -AuthSha256Forwarder::public_key() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - auto src_protocol = connection()->client_protocol(); - - const auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::server::AuthMethodData>( - dst_channel, dst_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::public_key")); - } - - if (!src_protocol->password().has_value()) { - stage(Stage::EncryptedPassword); - - return forward_server_to_client(); - } - - const auto msg = *msg_res; - - auto pubkey_res = Auth::public_key_from_pem(msg.auth_method_data()); - if (!pubkey_res) return recv_server_failed(pubkey_res.error()); - - // invalidates 'msg' - discard_current_msg(dst_channel, dst_protocol); - - auto nonce = initial_server_auth_data_; - - // if there is a trailing zero, strip it. - if (nonce.size() == Auth::kNonceLength + 1 && - nonce[Auth::kNonceLength] == 0x00) { - nonce = nonce.substr(0, Auth::kNonceLength); - } - - const auto encrypted_res = - Auth::rsa_encrypt_password(*pubkey_res, *src_protocol->password(), nonce); - if (!encrypted_res) return send_server_failed(encrypted_res.error()); - - if (auto &tr = tracer()) { - tr.trace( - Tracer::Event().stage("sha256_password::forward::encrypted_password")); - } - - const auto send_res = - Auth::send_encrypted_password(dst_channel, dst_protocol, *encrypted_res); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - - return Result::SendToServer; -} - -stdx::expected AuthSha256Forwarder::ok() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::ok")); - } - - return Result::Again; -} - -stdx::expected -AuthSha256Forwarder::error() { - stage(Stage::Done); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("sha256_password::forward::error")); - } - - return Result::Again; -} diff --git a/router/src/routing/src/classic_auth_sha256_password.h b/router/src/routing/src/classic_auth_sha256_password.h index 2a535e0e1096..d42be46fe2f4 100644 --- a/router/src/routing/src/classic_auth_sha256_password.h +++ b/router/src/routing/src/classic_auth_sha256_password.h @@ -25,15 +25,13 @@ #ifndef ROUTING_CLASSIC_AUTH_SHA256_PASSWORD_INCLUDED #define ROUTING_CLASSIC_AUTH_SHA256_PASSWORD_INCLUDED +#include #include #include #include -#include - #include "classic_auth.h" #include "classic_connection_base.h" -#include "forwarding_processor.h" #include "mysql/harness/stdx/expected.h" // low-level routings for sha256_password @@ -67,96 +65,4 @@ class AuthSha256Password : public AuthBase { static bool is_public_key(const std::string_view &data); }; -class AuthSha256Sender : public Processor { - public: - AuthSha256Sender(MysqlRoutingClassicConnectionBase *conn, - std::string initial_server_auth_data, std::string password) - : Processor(conn), - initial_server_auth_data_{std::move(initial_server_auth_data)}, - password_{std::move(password)} {} - - enum class Stage { - Init, - - Response, - - PublicKey, - - Error, - Ok, - - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - using Auth = AuthSha256Password; - - stdx::expected init(); - stdx::expected response(); - stdx::expected public_key(); - stdx::expected error(); - stdx::expected ok(); - - Stage stage_{Stage::Init}; - - std::string initial_server_auth_data_; - std::string password_; -}; - -// forwarder - -class AuthSha256Forwarder : public ForwardingProcessor { - public: - AuthSha256Forwarder(MysqlRoutingClassicConnectionBase *conn, - std::string initial_server_auth_data, - bool in_handshake = false) - : ForwardingProcessor(conn), - initial_server_auth_data_{std::move(initial_server_auth_data)}, - stage_{in_handshake ? Stage::Response : Stage::Init} {} - - enum class Stage { - Init, - - ClientData, - EncryptedPassword, - - PublicKeyResponse, - PublicKey, - - Response, - Error, - Ok, - - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - using Auth = AuthSha256Password; - - stdx::expected init(); - stdx::expected client_data(); - stdx::expected encrypted_password(); - stdx::expected response(); - stdx::expected public_key(); - stdx::expected public_key_response(); - stdx::expected error(); - stdx::expected ok(); - - stdx::expected send_password(); - - std::string initial_server_auth_data_; - - Stage stage_; -}; - #endif diff --git a/router/src/routing/src/classic_auth_sha256_password_forwarder.cc b/router/src/routing/src/classic_auth_sha256_password_forwarder.cc new file mode 100644 index 000000000000..6579071bcddd --- /dev/null +++ b/router/src/routing/src/classic_auth_sha256_password_forwarder.cc @@ -0,0 +1,437 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_auth_sha256_password_forwarder.h" + +#include "auth_digest.h" +#include "classic_frame.h" +#include "hexify.h" +#include "mysql/harness/logging/logging.h" +#include "mysqld_error.h" // mysql-server error-codes + +IMPORT_LOG_FUNCTIONS() + +using mysql_harness::hexify; + +// forwarder + +stdx::expected +AuthSha256Forwarder::process() { + switch (stage()) { + case Stage::Init: + return init(); + case Stage::ClientData: + return client_data(); + case Stage::EncryptedPassword: + return encrypted_password(); + case Stage::Response: + return response(); + case Stage::PublicKeyResponse: + return public_key_response(); + case Stage::PublicKey: + return public_key(); + case Stage::Error: + return error(); + case Stage::Ok: + return ok(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected AuthSha256Forwarder::init() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->client_channel(); + auto dst_protocol = connection()->client_protocol(); + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::switch")); + } + + auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::AuthMethodSwitch>( + dst_channel, dst_protocol, {Auth::kName, initial_server_auth_data_}); + if (!send_res) return send_client_failed(send_res.error()); + + stage(Stage::ClientData); + + return Result::SendToClient; +} + +stdx::expected +AuthSha256Forwarder::client_data() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->client_channel(); + auto src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::client::AuthMethodData>( + src_channel, src_protocol); + if (!msg_res) return recv_client_failed(msg_res.error()); + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::client_data:\n" + + hexify(msg_res->auth_method_data()))); + } + + if (src_channel->ssl() || + msg_res->auth_method_data() == Auth::kEmptyPassword) { + // password is null-terminated, remove it. + src_protocol->password(std::string( + AuthBase::strip_trailing_null(msg_res->auth_method_data()))); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::password:\n" + + hexify(*src_protocol->password()))); + } + + discard_current_msg(src_channel, src_protocol); + + return send_password(); + } else if (Auth::is_public_key_request(msg_res->auth_method_data())) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage( + "sha256_password::forward::public_key_request")); + } + + if (AuthBase::connection_has_public_key(connection())) { + // send the router's public-key to be able to decrypt the client's + // password. + discard_current_msg(src_channel, src_protocol); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::public_key")); + } + + auto pubkey_res = Auth::public_key_from_ssl_ctx_as_pem( + connection()->context().source_ssl_ctx()->get()); + if (!pubkey_res) { + auto ec = pubkey_res.error(); + + if (ec != std::errc::function_not_supported) { + return send_client_failed(ec); + } + + stage(Stage::Done); + + // couldn't get the public key, fail the auth. + auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::Error>( + src_channel, src_protocol, + {ER_ACCESS_DENIED_ERROR, "Access denied", "HY000"}); + if (!send_res) return send_client_failed(send_res.error()); + } else { + // send the router's public key to the client. + stage(Stage::EncryptedPassword); + + auto send_res = + Auth::send_public_key(src_channel, src_protocol, *pubkey_res); + if (!send_res) return send_client_failed(send_res.error()); + } + + return Result::SendToClient; + } else { + // client requested a public key, but router has no ssl-ctx + // (client-ssl-mode is DISABLED|PASSTHROUGH) + // + // If the server-connection is encrypted, the server will treat the + // public-key-request as an invalid password + // (as it isn't terminated by \0) + stage(Stage::PublicKeyResponse); + + return forward_client_to_server(); + } + } else { + discard_current_msg(src_channel, src_protocol); + + Tracer::Event().stage("sha256_password::forward::bad_message:\n" + + hexify(msg_res->auth_method_data())); + + return recv_client_failed(make_error_code(std::errc::bad_message)); + } +} + +// encrypted password from client to server. +stdx::expected +AuthSha256Forwarder::encrypted_password() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->client_channel(); + auto src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::client::AuthMethodData>( + src_channel, src_protocol); + if (!msg_res) return recv_client_failed(msg_res.error()); + + if (AuthBase::connection_has_public_key(connection())) { + auto nonce = src_protocol->auth_method_data(); + + // if there is a trailing zero, strip it. + if (nonce.size() == Auth::kNonceLength + 1 && + nonce[Auth::kNonceLength] == 0x00) { + nonce = nonce.substr(0, Auth::kNonceLength); + } + + auto recv_res = Auth::rsa_decrypt_password( + connection()->context().source_ssl_ctx()->get(), + msg_res->auth_method_data(), nonce); + if (!recv_res) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::decrypt:\n" + + recv_res.error().message())); + } + return recv_client_failed(recv_res.error()); + } + + src_protocol->password(*recv_res); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::password:\n" + + hexify(*src_protocol->password()))); + } + + discard_current_msg(src_channel, src_protocol); + + return send_password(); + } else { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::encrypted")); + } + + stage(Stage::Response); + + return forward_client_to_server(); + } +} + +// encrypted password from client to server. +stdx::expected +AuthSha256Forwarder::send_password() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_protocol = connection()->client_protocol(); + + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + if (dst_channel->ssl() || src_protocol->password()->empty()) { + // the server-side is encrypted (or the password is empty): + // + // send plaintext password + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage( + "sha256_password::forward::plaintext_password")); + } + + stage(Stage::Response); + + auto send_res = Auth::send_plaintext_password( + dst_channel, dst_protocol, src_protocol->password().value()); + if (!send_res) return send_server_failed(send_res.error()); + } else { + // the server is NOT encrypted: ask for the server's publickey + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage( + "sha256_password::forward::public_key_request")); + } + + stage(Stage::PublicKeyResponse); + + auto send_res = Auth::send_public_key_request(dst_channel, dst_protocol); + if (!send_res) return send_server_failed(send_res.error()); + } + + return Result::SendToServer; +} + +stdx::expected +AuthSha256Forwarder::response() { + // ERR|OK|EOF|other + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Ok = ClassicFrame::cmd_byte(), + Error = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::response")); + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug( + "received unexpected message from server in sha256-password-auth:\n%s", + hexify(recv_buf).c_str()); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +stdx::expected +AuthSha256Forwarder::public_key_response() { + // ERR|OK|EOF|other + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + AuthData = ClassicFrame::cmd_byte< + classic_protocol::message::server::AuthMethodData>(), + Error = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::AuthData: + stage(Stage::PublicKey); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::response")); + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug( + "received unexpected message from server in sha256-password-auth:\n%s", + hexify(recv_buf).c_str()); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +/* + * @pre + * a public-key request was sent to the server. + */ +stdx::expected +AuthSha256Forwarder::public_key() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + auto src_protocol = connection()->client_protocol(); + + const auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::server::AuthMethodData>( + dst_channel, dst_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::public_key")); + } + + if (!src_protocol->password().has_value()) { + stage(Stage::EncryptedPassword); + + return forward_server_to_client(); + } + + const auto msg = *msg_res; + + auto pubkey_res = Auth::public_key_from_pem(msg.auth_method_data()); + if (!pubkey_res) return recv_server_failed(pubkey_res.error()); + + // invalidates 'msg' + discard_current_msg(dst_channel, dst_protocol); + + auto nonce = initial_server_auth_data_; + + // if there is a trailing zero, strip it. + if (nonce.size() == Auth::kNonceLength + 1 && + nonce[Auth::kNonceLength] == 0x00) { + nonce = nonce.substr(0, Auth::kNonceLength); + } + + const auto encrypted_res = + Auth::rsa_encrypt_password(*pubkey_res, *src_protocol->password(), nonce); + if (!encrypted_res) return send_server_failed(encrypted_res.error()); + + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("sha256_password::forward::encrypted_password")); + } + + const auto send_res = + Auth::send_encrypted_password(dst_channel, dst_protocol, *encrypted_res); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + + return Result::SendToServer; +} + +stdx::expected AuthSha256Forwarder::ok() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::ok")); + } + + return Result::Again; +} + +stdx::expected +AuthSha256Forwarder::error() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::forward::error")); + } + + return Result::Again; +} diff --git a/router/src/routing/src/classic_auth_sha256_password_forwarder.h b/router/src/routing/src/classic_auth_sha256_password_forwarder.h new file mode 100644 index 000000000000..21ef6135af72 --- /dev/null +++ b/router/src/routing/src/classic_auth_sha256_password_forwarder.h @@ -0,0 +1,89 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_AUTH_SHA256_PASSWORD_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_AUTH_SHA256_PASSWORD_FORWARDER_INCLUDED + +#include +#include +#include + +#include "classic_auth.h" +#include "classic_auth_sha256_password.h" +#include "classic_connection_base.h" +#include "forwarding_processor.h" +#include "mysql/harness/stdx/expected.h" + +// forwarder + +class AuthSha256Forwarder : public ForwardingProcessor { + public: + AuthSha256Forwarder(MysqlRoutingClassicConnectionBase *conn, + std::string initial_server_auth_data, + bool in_handshake = false) + : ForwardingProcessor(conn), + initial_server_auth_data_{std::move(initial_server_auth_data)}, + stage_{in_handshake ? Stage::Response : Stage::Init} {} + + enum class Stage { + Init, + + ClientData, + EncryptedPassword, + + PublicKeyResponse, + PublicKey, + + Response, + Error, + Ok, + + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + using Auth = AuthSha256Password; + + stdx::expected init(); + stdx::expected client_data(); + stdx::expected encrypted_password(); + stdx::expected response(); + stdx::expected public_key(); + stdx::expected public_key_response(); + stdx::expected error(); + stdx::expected ok(); + + stdx::expected send_password(); + + std::string initial_server_auth_data_; + + Stage stage_; +}; + +#endif diff --git a/router/src/routing/src/classic_auth_sha256_password_sender.cc b/router/src/routing/src/classic_auth_sha256_password_sender.cc new file mode 100644 index 000000000000..dddaee2fcc2f --- /dev/null +++ b/router/src/routing/src/classic_auth_sha256_password_sender.cc @@ -0,0 +1,186 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_auth_sha256_password_sender.h" + +#include "auth_digest.h" +#include "classic_frame.h" +#include "hexify.h" +#include "mysql/harness/logging/logging.h" +#include "mysqld_error.h" // mysql-server error-codes + +IMPORT_LOG_FUNCTIONS() + +// sender + +stdx::expected AuthSha256Sender::process() { + switch (stage()) { + case Stage::Init: + return init(); + case Stage::Response: + return response(); + case Stage::PublicKey: + return public_key(); + case Stage::Error: + return error(); + case Stage::Ok: + return ok(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected AuthSha256Sender::init() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + if (dst_channel->ssl() || password_.empty()) { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("sha256_password::sender::plaintext_password")); + } + + auto send_res = ClassicFrame::send_msg( + dst_channel, dst_protocol, + classic_protocol::borrowed::message::client::AuthMethodData{password_ + + '\0'}); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + } else { + if (auto &tr = tracer()) { + tr.trace( + Tracer::Event().stage("sha256_password::sender::public_key_request")); + } + auto send_res = Auth::send_public_key_request(dst_channel, dst_protocol); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::PublicKey); + } + + return Result::SendToServer; +} + +stdx::expected +AuthSha256Sender::response() { + // ERR|OK|EOF|other + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // ensure the recv_buf has at last frame-header (+ msg-byte) + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Ok = ClassicFrame::cmd_byte(), + Error = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + // if there is another packet, dump its payload for now. + auto &recv_buf = src_channel->recv_plain_view(); + + // get as much data of the current frame from the recv-buffers to log it. + (void)ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + + log_debug("received unexpected message from server in sha256-auth:\n%s", + mysql_harness::hexify(recv_buf).c_str()); + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +stdx::expected +AuthSha256Sender::public_key() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::server::AuthMethodData>( + dst_channel, dst_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + auto msg = *msg_res; + + auto pubkey_res = Auth::public_key_from_pem(msg.auth_method_data()); + if (!pubkey_res) return recv_server_failed(pubkey_res.error()); + + // discard _after_ msg. is used as msg borrows from the dst_channel's + // recv_buffer. + discard_current_msg(dst_channel, dst_protocol); + + auto nonce = initial_server_auth_data_; + + // if there is a trailing zero, strip it. + if (nonce.size() == Auth::kNonceLength + 1 && + nonce[Auth::kNonceLength] == 0x00) { + nonce = nonce.substr(0, Auth::kNonceLength); + } + + auto encrypted_res = + Auth::rsa_encrypt_password(*pubkey_res, password_, nonce); + if (!encrypted_res) return send_server_failed(encrypted_res.error()); + + auto send_res = + Auth::send_encrypted_password(dst_channel, dst_protocol, *encrypted_res); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + + return Result::SendToServer; +} + +stdx::expected AuthSha256Sender::ok() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::sender::ok")); + } + + return Result::Again; +} + +stdx::expected AuthSha256Sender::error() { + stage(Stage::Done); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("sha256_password::sender::error")); + } + + return Result::Again; +} diff --git a/router/src/routing/src/classic_auth_sha256_password_sender.h b/router/src/routing/src/classic_auth_sha256_password_sender.h new file mode 100644 index 000000000000..42532b10bad4 --- /dev/null +++ b/router/src/routing/src/classic_auth_sha256_password_sender.h @@ -0,0 +1,78 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_AUTH_SHA256_PASSWORD_SENDER_INCLUDED +#define ROUTING_CLASSIC_AUTH_SHA256_PASSWORD_SENDER_INCLUDED + +#include +#include +#include + +#include "classic_auth_sha256_password.h" +#include "classic_connection_base.h" +#include "forwarding_processor.h" +#include "mysql/harness/stdx/expected.h" + +class AuthSha256Sender : public Processor { + public: + AuthSha256Sender(MysqlRoutingClassicConnectionBase *conn, + std::string initial_server_auth_data, std::string password) + : Processor(conn), + initial_server_auth_data_{std::move(initial_server_auth_data)}, + password_{std::move(password)} {} + + enum class Stage { + Init, + + Response, + + PublicKey, + + Error, + Ok, + + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + using Auth = AuthSha256Password; + + stdx::expected init(); + stdx::expected response(); + stdx::expected public_key(); + stdx::expected error(); + stdx::expected ok(); + + Stage stage_{Stage::Init}; + + std::string initial_server_auth_data_; + std::string password_; +}; + +#endif diff --git a/router/src/routing/src/classic_binlog_dump.cc b/router/src/routing/src/classic_binlog_dump_forwarder.cc similarity index 98% rename from router/src/routing/src/classic_binlog_dump.cc rename to router/src/routing/src/classic_binlog_dump_forwarder.cc index f0f2c09ba166..58277b14cca9 100644 --- a/router/src/routing/src/classic_binlog_dump.cc +++ b/router/src/routing/src/classic_binlog_dump_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_binlog_dump.h" +#include "classic_binlog_dump_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_binlog_dump.h b/router/src/routing/src/classic_binlog_dump_forwarder.h similarity index 92% rename from router/src/routing/src/classic_binlog_dump.h rename to router/src/routing/src/classic_binlog_dump_forwarder.h index 236681ae34e2..d173bd4e1d85 100644 --- a/router/src/routing/src/classic_binlog_dump.h +++ b/router/src/routing/src/classic_binlog_dump_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_BINLOG_DUMP_INCLUDED -#define ROUTING_CLASSIC_BINLOG_DUMP_INCLUDED +#ifndef ROUTING_CLASSIC_BINLOG_DUMP_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_BINLOG_DUMP_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_change_user_forwarder.cc b/router/src/routing/src/classic_change_user_forwarder.cc new file mode 100644 index 000000000000..ebbeb556d1b2 --- /dev/null +++ b/router/src/routing/src/classic_change_user_forwarder.cc @@ -0,0 +1,283 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_change_user_forwarder.h" + +#include + +#include "classic_auth.h" +#include "classic_auth_caching_sha2.h" +#include "classic_auth_cleartext.h" +#include "classic_auth_forwarder.h" +#include "classic_auth_native.h" +#include "classic_auth_sha256_password.h" +#include "classic_change_user_sender.h" +#include "classic_connect.h" +#include "classic_connection_base.h" +#include "classic_forwarder.h" +#include "classic_frame.h" +#include "classic_greeting_forwarder.h" // ServerGreetor +#include "classic_query_sender.h" +#include "mysql/harness/logging/logging.h" +#include "mysql/harness/stdx/expected.h" +#include "mysql/harness/tls_error.h" +#include "mysqld_error.h" // mysql-server error-codes +#include "openssl_version.h" +#include "tracer.h" + +IMPORT_LOG_FUNCTIONS() + +using namespace std::string_literals; +using namespace std::string_view_literals; + +/** + * forward the change-user message flow. + * + * Expected overall flow: + * + * @code + * c->s: COM_CHANGE_USER + * alt fast-path + * alt + * c<-s: Error + * else + * c<-s: Ok + * end + * else auth-method-switch + * c<-s: auth-method-switch + * c->s: auth-method-data + * loop more data + * c<-s: auth-method-data + * opt + * c->s: auth-method-data + * end + * end + * alt + * c<-s: Error + * else + * c<-s: Ok + * end + * end + * @endcode + * + * If there is no server connection, it is created on demand. + */ +stdx::expected +ChangeUserForwarder::process() { + switch (stage()) { + case Stage::Command: + return command(); + case Stage::Connect: + return connect(); + case Stage::Connected: + return connected(); + case Stage::Response: + return response(); + case Stage::Ok: + return ok(); + case Stage::Error: + return error(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected +ChangeUserForwarder::command() { + auto *socket_splicer = connection()->socket_splicer(); + auto *src_channel = socket_splicer->client_channel(); + auto *src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::client::ChangeUser>( + src_channel, src_protocol, src_protocol->server_capabilities()); + if (!msg_res) { + if (msg_res.error().category() == + make_error_code(classic_protocol::codec_errc::invalid_input) + .category()) { + // a codec error. + + discard_current_msg(src_channel, src_protocol); + + auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::Error>( + src_channel, src_protocol, {1047, "Unknown command", "08S01"}); + if (!send_res) return send_client_failed(send_res.error()); + + stage(Stage::Done); + return Result::SendToClient; + } + return recv_client_failed(msg_res.error()); + } + + src_protocol->username(std::string(msg_res->username())); + src_protocol->schema(std::string(msg_res->schema())); + src_protocol->attributes(std::string(msg_res->attributes())); + src_protocol->password(std::nullopt); + src_protocol->auth_method_name(std::string(msg_res->auth_method_name())); + + discard_current_msg(src_channel, src_protocol); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("change_user::command")); + } + + auto &server_conn = connection()->socket_splicer()->server_conn(); + if (!server_conn.is_open()) { + stage(Stage::Connect); + } else { + // connection to the server exists, create a new ChangeUser command (don't + // forward the client's as is) as the attributes need to be modified. + connection()->push_processor(std::make_unique( + connection(), true /* in-handshake */)); + + stage(Stage::Response); + } + + return Result::Again; +} + +stdx::expected +ChangeUserForwarder::connect() { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("change_user::connect")); + } + + stage(Stage::Connected); + + // connect or take connection from pool + // + // don't use LazyConnector here as it would call authenticate with the old + // user and then switch to the new one in a 2nd ChangeUser. + connection()->push_processor( + std::make_unique(connection())); + + return Result::Again; +} + +stdx::expected +ChangeUserForwarder::connected() { + auto &server_conn = connection()->socket_splicer()->server_conn(); + auto server_protocol = connection()->server_protocol(); + + if (!server_conn.is_open()) { + // Connector sent an server::Error already. + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->client_channel(); + auto src_protocol = connection()->client_protocol(); + + // take the client::command from the connection. + auto recv_res = + ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + if (!recv_res) return recv_client_failed(recv_res.error()); + + discard_current_msg(src_channel, src_protocol); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("change_user::connect::error")); + } + + stage(Stage::Done); + return Result::Again; + } + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("change_user::connected")); + } + + if (server_protocol->server_greeting()) { + // from pool. + connection()->push_processor( + std::make_unique(connection(), true)); + } else { + // connector, but not greeted yet. + connection()->push_processor( + std::make_unique(connection(), true)); + } + + stage(Stage::Response); + return Result::Again; +} + +stdx::expected +ChangeUserForwarder::response() { + // ChangeUserSender will set ->authenticated if it succeed. + if (!connection()->authenticated()) { + stage(Stage::Error); + } else { + stage(Stage::Ok); + } + + return Result::Again; +} + +stdx::expected ChangeUserForwarder::ok() { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("change_user::ok")); + } + + // allow connection sharing again. + connection()->connection_sharing_allowed_reset(); + + // clear the warnings + connection()->execution_context().diagnostics_area().warnings().clear(); + + if (connection()->context().connection_sharing() && + connection()->greeting_from_router()) { + // if connection sharing is enabled in the config, enable the + // session-tracker. + connection()->push_processor(std::make_unique(connection(), R"( +SET @@SESSION.session_track_schema = 'ON', + @@SESSION.session_track_system_variables = '*', + @@SESSION.session_track_transaction_info = 'CHARACTERISTICS', + @@SESSION.session_track_gtids = 'OWN_GTID', + @@SESSION.session_track_state_change = 'ON')")); + + stage(Stage::Done); + } else { + stage(Stage::Done); + } + + return Result::Again; +} + +stdx::expected +ChangeUserForwarder::error() { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("change_user::error")); + } + + auto *socket_splicer = connection()->socket_splicer(); + + // after the error the server will close the server connection. + auto &server_conn = socket_splicer->server_conn(); + + (void)server_conn.close(); + + stage(Stage::Done); + + return Result::Again; +} diff --git a/router/src/routing/src/classic_change_user_forwarder.h b/router/src/routing/src/classic_change_user_forwarder.h new file mode 100644 index 000000000000..8adbb93800ce --- /dev/null +++ b/router/src/routing/src/classic_change_user_forwarder.h @@ -0,0 +1,63 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_CHANGE_USER_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_CHANGE_USER_FORWARDER_INCLUDED + +#include "forwarding_processor.h" + +/** + * forwards COM_CHANGE_USER from client to the server. + */ +class ChangeUserForwarder : public ForwardingProcessor { + public: + using ForwardingProcessor::ForwardingProcessor; + + enum class Stage { + Command, + Connect, + Connected, + Response, + Ok, + Error, + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + stdx::expected command(); + stdx::expected connect(); + stdx::expected connected(); + stdx::expected response(); + stdx::expected ok(); + stdx::expected error(); + + Stage stage_{Stage::Command}; +}; + +#endif diff --git a/router/src/routing/src/classic_change_user.cc b/router/src/routing/src/classic_change_user_sender.cc similarity index 66% rename from router/src/routing/src/classic_change_user.cc rename to router/src/routing/src/classic_change_user_sender.cc index fa25b464b37c..4feb2e39635b 100644 --- a/router/src/routing/src/classic_change_user.cc +++ b/router/src/routing/src/classic_change_user_sender.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_change_user.h" +#include "classic_change_user_sender.h" #include @@ -34,10 +34,8 @@ #include "classic_auth_sha256_password.h" #include "classic_connect.h" #include "classic_connection_base.h" -#include "classic_forwarder.h" #include "classic_frame.h" -#include "classic_greeting.h" -#include "classic_query.h" +#include "classic_query_sender.h" #include "hexify.h" #include "mysql/harness/logging/logging.h" #include "mysql/harness/stdx/expected.h" @@ -53,237 +51,6 @@ using mysql_harness::hexify; using namespace std::string_literals; using namespace std::string_view_literals; -/** - * forward the change-user message flow. - * - * Expected overall flow: - * - * @code - * c->s: COM_CHANGE_USER - * alt fast-path - * alt - * c<-s: Error - * else - * c<-s: Ok - * end - * else auth-method-switch - * c<-s: auth-method-switch - * c->s: auth-method-data - * loop more data - * c<-s: auth-method-data - * opt - * c->s: auth-method-data - * end - * end - * alt - * c<-s: Error - * else - * c<-s: Ok - * end - * end - * @endcode - * - * If there is no server connection, it is created on demand. - */ -stdx::expected -ChangeUserForwarder::process() { - switch (stage()) { - case Stage::Command: - return command(); - case Stage::Connect: - return connect(); - case Stage::Connected: - return connected(); - case Stage::Response: - return response(); - case Stage::Ok: - return ok(); - case Stage::Error: - return error(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected -ChangeUserForwarder::command() { - auto *socket_splicer = connection()->socket_splicer(); - auto *src_channel = socket_splicer->client_channel(); - auto *src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::client::ChangeUser>( - src_channel, src_protocol, src_protocol->server_capabilities()); - if (!msg_res) { - if (msg_res.error().category() == - make_error_code(classic_protocol::codec_errc::invalid_input) - .category()) { - // a codec error. - - discard_current_msg(src_channel, src_protocol); - - auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::Error>( - src_channel, src_protocol, {1047, "Unknown command", "08S01"}); - if (!send_res) return send_client_failed(send_res.error()); - - stage(Stage::Done); - return Result::SendToClient; - } - return recv_client_failed(msg_res.error()); - } - - src_protocol->username(std::string(msg_res->username())); - src_protocol->schema(std::string(msg_res->schema())); - src_protocol->attributes(std::string(msg_res->attributes())); - src_protocol->password(std::nullopt); - src_protocol->auth_method_name(std::string(msg_res->auth_method_name())); - - discard_current_msg(src_channel, src_protocol); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("change_user::command")); - } - - auto &server_conn = connection()->socket_splicer()->server_conn(); - if (!server_conn.is_open()) { - stage(Stage::Connect); - } else { - // connection to the server exists, create a new ChangeUser command (don't - // forward the client's as is) as the attributes need to be modified. - connection()->push_processor(std::make_unique( - connection(), true /* in-handshake */)); - - stage(Stage::Response); - } - - return Result::Again; -} - -stdx::expected -ChangeUserForwarder::connect() { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("change_user::connect")); - } - - stage(Stage::Connected); - - // connect or take connection from pool - // - // don't use LazyConnector here as it would call authenticate with the old - // user and then switch to the new one in a 2nd ChangeUser. - connection()->push_processor( - std::make_unique(connection())); - - return Result::Again; -} - -stdx::expected -ChangeUserForwarder::connected() { - auto &server_conn = connection()->socket_splicer()->server_conn(); - auto server_protocol = connection()->server_protocol(); - - if (!server_conn.is_open()) { - // Connector sent an server::Error already. - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->client_channel(); - auto src_protocol = connection()->client_protocol(); - - // take the client::command from the connection. - auto recv_res = - ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - if (!recv_res) return recv_client_failed(recv_res.error()); - - discard_current_msg(src_channel, src_protocol); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("change_user::connect::error")); - } - - stage(Stage::Done); - return Result::Again; - } - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("change_user::connected")); - } - - if (server_protocol->server_greeting()) { - // from pool. - connection()->push_processor( - std::make_unique(connection(), true)); - } else { - // connector, but not greeted yet. - connection()->push_processor( - std::make_unique(connection(), true)); - } - - stage(Stage::Response); - return Result::Again; -} - -stdx::expected -ChangeUserForwarder::response() { - // ChangeUserSender will set ->authenticated if it succeed. - if (!connection()->authenticated()) { - stage(Stage::Error); - } else { - stage(Stage::Ok); - } - - return Result::Again; -} - -stdx::expected ChangeUserForwarder::ok() { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("change_user::ok")); - } - - // allow connection sharing again. - connection()->connection_sharing_allowed_reset(); - - // clear the warnings - connection()->execution_context().diagnostics_area().warnings().clear(); - - if (connection()->context().connection_sharing() && - connection()->greeting_from_router()) { - // if connection sharing is enabled in the config, enable the - // session-tracker. - connection()->push_processor(std::make_unique(connection(), R"( -SET @@SESSION.session_track_schema = 'ON', - @@SESSION.session_track_system_variables = '*', - @@SESSION.session_track_transaction_info = 'CHARACTERISTICS', - @@SESSION.session_track_gtids = 'OWN_GTID', - @@SESSION.session_track_state_change = 'ON')")); - - stage(Stage::Done); - } else { - stage(Stage::Done); - } - - return Result::Again; -} - -stdx::expected -ChangeUserForwarder::error() { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("change_user::error")); - } - - auto *socket_splicer = connection()->socket_splicer(); - - // after the error the server will close the server connection. - auto &server_conn = socket_splicer->server_conn(); - - (void)server_conn.close(); - - stage(Stage::Done); - - return Result::Again; -} - stdx::expected ChangeUserSender::process() { switch (stage()) { case Stage::Command: diff --git a/router/src/routing/src/classic_change_user.h b/router/src/routing/src/classic_change_user_sender.h similarity index 68% rename from router/src/routing/src/classic_change_user.h rename to router/src/routing/src/classic_change_user_sender.h index 627030cb0afd..3e0299c8e456 100644 --- a/router/src/routing/src/classic_change_user.h +++ b/router/src/routing/src/classic_change_user_sender.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,44 +22,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_CHANGE_USER_INCLUDED -#define ROUTING_CLASSIC_CHANGE_USER_INCLUDED +#ifndef ROUTING_CLASSIC_CHANGE_USER_SENDER_INCLUDED +#define ROUTING_CLASSIC_CHANGE_USER_SENDER_INCLUDED #include "forwarding_processor.h" -/** - * forwards COM_CHANGE_USER from client to the server. - */ -class ChangeUserForwarder : public ForwardingProcessor { - public: - using ForwardingProcessor::ForwardingProcessor; - - enum class Stage { - Command, - Connect, - Connected, - Response, - Ok, - Error, - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - stdx::expected command(); - stdx::expected connect(); - stdx::expected connected(); - stdx::expected response(); - stdx::expected ok(); - stdx::expected error(); - - Stage stage_{Stage::Command}; -}; - /** * sends COM_CHANGE_USER from router to the server. */ diff --git a/router/src/routing/src/classic_clone.cc b/router/src/routing/src/classic_clone_forwarder.cc similarity index 99% rename from router/src/routing/src/classic_clone.cc rename to router/src/routing/src/classic_clone_forwarder.cc index 66f55b9a4e04..b1849c879416 100644 --- a/router/src/routing/src/classic_clone.cc +++ b/router/src/routing/src/classic_clone_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_clone.h" +#include "classic_clone_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_clone.h b/router/src/routing/src/classic_clone_forwarder.h similarity index 94% rename from router/src/routing/src/classic_clone.h rename to router/src/routing/src/classic_clone_forwarder.h index a42f163833c8..5eb17a899a4b 100644 --- a/router/src/routing/src/classic_clone.h +++ b/router/src/routing/src/classic_clone_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_CLONE_INCLUDED -#define ROUTING_CLASSIC_CLONE_INCLUDED +#ifndef ROUTING_CLASSIC_CLONE_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_CLONE_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_command.cc b/router/src/routing/src/classic_command.cc index ec0a3ae76269..0014abbba11e 100644 --- a/router/src/routing/src/classic_command.cc +++ b/router/src/routing/src/classic_command.cc @@ -28,28 +28,29 @@ #include // make_unique #include -#include "classic_binlog_dump.h" -#include "classic_change_user.h" -#include "classic_clone.h" +#include "classic_binlog_dump_forwarder.h" +#include "classic_change_user_forwarder.h" +#include "classic_clone_forwarder.h" #include "classic_connection_base.h" #include "classic_frame.h" -#include "classic_init_schema.h" -#include "classic_kill.h" -#include "classic_list_fields.h" -#include "classic_ping.h" -#include "classic_query.h" -#include "classic_quit.h" -#include "classic_register_replica.h" -#include "classic_reload.h" -#include "classic_reset_connection.h" -#include "classic_set_option.h" -#include "classic_statistics.h" -#include "classic_stmt_close.h" -#include "classic_stmt_execute.h" -#include "classic_stmt_fetch.h" -#include "classic_stmt_param_append_data.h" -#include "classic_stmt_prepare.h" -#include "classic_stmt_reset.h" +#include "classic_init_schema_forwarder.h" +#include "classic_kill_forwarder.h" +#include "classic_list_fields_forwarder.h" +#include "classic_ping_forwarder.h" +#include "classic_query_forwarder.h" +#include "classic_query_sender.h" +#include "classic_quit_forwarder.h" +#include "classic_register_replica_forwarder.h" +#include "classic_reload_forwarder.h" +#include "classic_reset_connection_forwarder.h" +#include "classic_set_option_forwarder.h" +#include "classic_statistics_forwarder.h" +#include "classic_stmt_close_forwarder.h" +#include "classic_stmt_execute_forwarder.h" +#include "classic_stmt_fetch_forwarder.h" +#include "classic_stmt_param_append_data_forwarder.h" +#include "classic_stmt_prepare_forwarder.h" +#include "classic_stmt_reset_forwarder.h" #include "harness_assert.h" #include "hexify.h" #include "mysql/harness/logging/logging.h" diff --git a/router/src/routing/src/classic_flow.cc b/router/src/routing/src/classic_flow.cc index f4ab71737e07..fc0c7839bc21 100644 --- a/router/src/routing/src/classic_flow.cc +++ b/router/src/routing/src/classic_flow.cc @@ -26,8 +26,7 @@ #include "classic_command.h" #include "classic_connection_base.h" -#include "classic_greeting.h" -#include "classic_query.h" +#include "classic_greeting_receiver.h" stdx::expected FlowProcessor::process() { switch (stage()) { diff --git a/router/src/routing/src/classic_greeting.cc b/router/src/routing/src/classic_greeting_forwarder.cc similarity index 76% rename from router/src/routing/src/classic_greeting.cc rename to router/src/routing/src/classic_greeting_forwarder.cc index bd9fd1b5cfc6..909b12a9efc8 100644 --- a/router/src/routing/src/classic_greeting.cc +++ b/router/src/routing/src/classic_greeting_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_greeting.h" +#include "classic_greeting_forwarder.h" #include #include @@ -39,7 +39,7 @@ #include "classic_auth_forwarder.h" #include "classic_auth_native.h" #include "classic_auth_sha256_password.h" -#include "classic_change_user.h" +#include "classic_change_user_forwarder.h" #include "classic_connect.h" #include "classic_connection_base.h" #include "classic_forwarder.h" @@ -55,28 +55,16 @@ #include "mysqld_error.h" // mysql-server error-codes #include "mysqlrouter/classic_protocol_constants.h" #include "mysqlrouter/connection_base.h" -#include "openssl_version.h" -#include "processor.h" #include "sql/server_component/mysql_command_services_imp.h" #include "tracer.h" -using namespace std::string_literals; - IMPORT_LOG_FUNCTIONS() using mysql_harness::hexify; +using namespace std::string_literals; using namespace std::string_view_literals; -static constexpr const std::array supported_authentication_methods{ - AuthCachingSha2Password::kName, - AuthNativePassword::kName, - AuthCleartextPassword::kName, - AuthSha256Password::kName, -}; - -static constexpr const bool kCapturePlaintextPassword{true}; - /** * router specific connection attributes. * @@ -149,192 +137,6 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) { } } -stdx::expected ClientGreetor::process() { - switch (stage()) { - case Stage::Init: - return init(); - case Stage::ServerGreeting: - return server_greeting(); - case Stage::ServerFirstGreeting: - return server_first_greeting(); - case Stage::ClientGreeting: - return client_greeting(); - case Stage::TlsAcceptInit: - return tls_accept_init(); - case Stage::TlsAccept: - return tls_accept(); - case Stage::ClientGreetingAfterTls: - return client_greeting_after_tls(); - case Stage::RequestPlaintextPassword: - return request_plaintext_password(); - case Stage::PlaintextPassword: - return plaintext_password(); - - case Stage::Accepted: - return accepted(); - - case Stage::Authenticated: - return authenticated(); - - // the two exit-stages: - // - Error - // - Ok - case Stage::Error: - return error(); - case Stage::Ok: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected ClientGreetor::error() { - // after the greetings error has been sent to the client. - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("client::greeting::error")); - } - - auto &client_conn = connection()->socket_splicer()->client_conn(); - - (void)client_conn.cancel(); - (void)client_conn.shutdown(net::socket_base::shutdown_both); - - return Result::Done; -} - -stdx::expected ClientGreetor::init() { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("client::init")); - } - - if (!connection()->greeting_from_router()) { - stage(Stage::ServerFirstGreeting); - - connection()->push_processor( - std::make_unique(connection())); - } else { - stage(Stage::ServerGreeting); - } - return Result::Again; -} - -/** - * client<-router: server::greeting. - */ -stdx::expected -ClientGreetor::server_greeting() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->client_channel(); - auto dst_protocol = connection()->client_protocol(); - - classic_protocol::capabilities::value_type router_capabilities( - classic_protocol::capabilities::long_password | - classic_protocol::capabilities::found_rows | - classic_protocol::capabilities::long_flag | - classic_protocol::capabilities::connect_with_schema | - classic_protocol::capabilities::no_schema | - // compress (not yet) - classic_protocol::capabilities::odbc | - classic_protocol::capabilities::local_files | - // ignore_space (client only) - classic_protocol::capabilities::protocol_41 | - classic_protocol::capabilities::interactive | - // ssl (below) - // ignore sigpipe (client-only) - classic_protocol::capabilities::transactions | - classic_protocol::capabilities::secure_connection | - classic_protocol::capabilities::multi_statements | - classic_protocol::capabilities::multi_results | - classic_protocol::capabilities::ps_multi_results | - classic_protocol::capabilities::plugin_auth | - classic_protocol::capabilities::connect_attributes | - classic_protocol::capabilities::client_auth_method_data_varint | - classic_protocol::capabilities::expired_passwords | - classic_protocol::capabilities::session_track | - classic_protocol::capabilities::text_result_with_session_tracking | - classic_protocol::capabilities::optional_resultset_metadata - // compress_zstd (not yet) - ); - - if (connection()->source_ssl_mode() != SslMode::kDisabled) { - router_capabilities.set(classic_protocol::capabilities::pos::ssl); - } - - dst_protocol->server_capabilities(router_capabilities); - - auto random_auth_method_data = []() { - std::random_device rd; - std::mt19937 gen(rd()); - // Scrambles defined as 7-bit: 1..127 ... no \0 chars - std::uniform_int_distribution<> distrib(1, 127); - - std::string scramble; - scramble.resize(20 + 1); // 20 random data + [trailing, explicit \0] - - for (size_t n{}; n < scramble.size() - 1; ++n) { - scramble[n] = distrib(gen); - } - - return scramble; - }; - - auto server_greeting_version = []() { - using namespace std::string_literals; - - return MYSQL_ROUTER_VERSION "-router"s; - }; - - classic_protocol::message::server::Greeting server_greeting_msg{ - 10, // protocol - server_greeting_version(), // version - 0, // connection-id - random_auth_method_data(), // auth-method-data - dst_protocol->server_capabilities(), // server-caps - 255, // 8.0.20 sends 0xff here - classic_protocol::status::autocommit, // status-flags - std::string(AuthCachingSha2Password::kName), // auth-method-name - }; - - auto send_res = - ClassicFrame::send_msg(dst_channel, dst_protocol, server_greeting_msg, - {/* no shared caps yet */}); - if (!send_res) return send_client_failed(send_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("server::greeting")); - } - - dst_protocol->auth_method_data(server_greeting_msg.auth_method_data()); - dst_protocol->server_greeting(server_greeting_msg); - - stage(Stage::ClientGreeting); - return Result::SendToClient; -} - -/** - * client<-router: server::greeting. - */ -stdx::expected -ClientGreetor::server_first_greeting() { - auto *socket_splicer = connection()->socket_splicer(); - - // ServerFirstGreetor either - // - sent the server-greeting to the client and - // left the server connection open, or - // - sent the error to the client and - // closed the connection. - - auto &server_conn = socket_splicer->server_conn(); - - if (server_conn.is_open()) { - stage(Stage::ClientGreeting); - } else { - stage(Stage::Error); - } - - return Result::Again; -} - static void adjust_supported_capabilities( SslMode source_ssl_mode, SslMode dest_ssl_mode, classic_protocol::capabilities::value_type &caps) { @@ -374,28 +176,6 @@ static void adjust_supported_capabilities( } } -static bool client_ssl_mode_is_satisfied( - SslMode client_ssl_mode, - classic_protocol::capabilities::value_type shared_capabilities) { - if ((client_ssl_mode == SslMode::kRequired) && - !shared_capabilities.test(classic_protocol::capabilities::pos::ssl)) { - return false; - } - - return true; -} - -static bool client_compress_is_satisfied( - classic_protocol::capabilities::value_type client_capabilities, - classic_protocol::capabilities::value_type shared_capabilities) { - // client enabled "zlib-compress" without checking the server's caps. - // - // fail the connect. - return !( - client_capabilities.test(classic_protocol::capabilities::pos::compress) && - !shared_capabilities.test(classic_protocol::capabilities::pos::compress)); -} - static stdx::expected send_ssl_connection_error_msg( Channel *dst_channel, ClassicProtocolState *dst_protocol, const std::string &msg) { @@ -404,290 +184,6 @@ static stdx::expected send_ssl_connection_error_msg( dst_channel, dst_protocol, {CR_SSL_CONNECTION_ERROR, msg}); } -/** - * handle client greeting. - */ -stdx::expected -ClientGreetor::client_greeting() { - auto *src_channel = connection()->socket_splicer()->client_channel(); - auto *src_protocol = connection()->client_protocol(); - - auto msg_res = - ClassicFrame::recv_msg( - src_channel, src_protocol, src_protocol->server_capabilities()); - if (!msg_res) return recv_client_failed(msg_res.error()); - - auto msg = std::move(*msg_res); - - if (src_protocol->seq_id() != 1) { - // client-greeting has seq-id 1 - return recv_client_failed(make_error_code(std::errc::bad_message)); - } - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("client::greeting")); - } - - src_protocol->client_greeting(msg); - src_protocol->client_capabilities(msg.capabilities()); - src_protocol->auth_method_name(msg.auth_method_name()); - src_protocol->username(msg.username()); - src_protocol->schema(msg.schema()); - src_protocol->attributes(msg.attributes()); - - if (!client_ssl_mode_is_satisfied(connection()->source_ssl_mode(), - src_protocol->shared_capabilities())) { - // config says: client->router MUST be encrypted, but client didn't set - // the SSL cap. - // - const auto send_res = send_ssl_connection_error_msg( - src_channel, src_protocol, - "SSL connection error: SSL is required from client"); - if (!send_res) return send_client_failed(send_res.error()); - - stage(Stage::Error); - return Result::SendToClient; - } - - // fail connection from buggy clients that set the compress-cap without - // checking the server's capabilities. - if (!client_compress_is_satisfied(src_protocol->client_capabilities(), - src_protocol->shared_capabilities())) { - const auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::Error>( - src_channel, src_protocol, - {ER_WRONG_COMPRESSION_ALGORITHM_CLIENT, - "Compression not supported by router."}); - if (!send_res) return send_client_failed(send_res.error()); - - stage(Stage::Error); - return Result::SendToClient; - } - - // remove the frame and message from the recv-buffer - discard_current_msg(src_channel, src_protocol); - - if (!src_protocol->shared_capabilities().test( - classic_protocol::capabilities::pos::ssl)) { - // client wants to stay with plaintext - - if (msg.auth_method_data() == "\x00"sv) { - // password is empty. - src_protocol->password(""); - } else { - const bool client_conn_is_secure = - connection()->socket_splicer()->client_conn().is_secure_transport(); - - if (client_conn_is_secure && - src_protocol->auth_method_name() == AuthCachingSha2Password::kName) { - stage(Stage::RequestPlaintextPassword); - return Result::Again; - } - } - - stage(Stage::Accepted); - return Result::Again; - } else if (connection()->source_ssl_mode() == SslMode::kPassthrough) { - stage(Stage::Accepted); - return Result::Again; - } else { - stage(Stage::TlsAcceptInit); - return Result::Again; - } -} - -stdx::expected -ClientGreetor::tls_accept_init() { - auto *socket_splicer = connection()->socket_splicer(); - auto *src_channel = socket_splicer->client_channel(); - - src_channel->is_tls(true); - - auto *ssl_ctx = connection()->context().source_ssl_ctx()->get(); - // tls <-> (any) - if (ssl_ctx == nullptr) { - // shouldn't happen. But if it does, close the connection. - log_warning("failed to create SSL_CTX"); - return recv_client_failed(make_error_code(std::errc::invalid_argument)); - } - src_channel->init_ssl(ssl_ctx); - - SSL_set_app_data(src_channel->ssl(), connection()); - SSL_set_info_callback(src_channel->ssl(), ssl_info_cb); - - stage(Stage::TlsAccept); - return Result::Again; -} - -stdx::expected ClientGreetor::tls_accept() { - auto *socket_splicer = connection()->socket_splicer(); - auto *client_channel = socket_splicer->client_channel(); - - if (!client_channel->tls_init_is_finished()) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("tls::accept")); - } - - auto res = socket_splicer->tls_accept(); - if (!res) { - const auto ec = res.error(); - - // the send-buffer contains an alert message telling the client why the - // accept failed. - if (!client_channel->send_buffer().empty()) { - if (ec != TlsErrc::kWantRead) { - log_debug("tls-accept failed: %s", ec.message().c_str()); - - stage(Stage::Error); - } - return Result::SendToClient; - } - - if (ec == TlsErrc::kWantRead) return Result::RecvFromClient; - - log_fatal_error_code("tls-accept failed", ec); - - return recv_client_failed(ec); - } - } - - if (auto &tr = tracer()) { - auto *ssl = client_channel->ssl(); - std::ostringstream oss; - oss << "tls::accept::ok: " << SSL_get_version(ssl); - oss << " using " << SSL_get_cipher_name(ssl); - - if (SSL_session_reused(ssl) != 0) { - oss << ", session_reused"; - } - - tr.trace(Tracer::Event().stage(oss.str())); - } - - stage(Stage::ClientGreetingAfterTls); - - // after tls_accept() there may still be data in the send-buffer that must - // be sent. - if (!client_channel->send_buffer().empty()) { - return Result::SendToClient; - } - // TLS is accepted, more client greeting should follow. - - return Result::Again; -} - -/** - * check if the authentication method is supported. - * - * @see supported_authentication_methods - * - * @retval true auth_method_name is supported - * @retval false auth_method_name is not supported - */ -static bool authentication_method_is_supported( - const std::string &auth_method_name) { - auto it = std::find(supported_authentication_methods.begin(), - supported_authentication_methods.end(), auth_method_name); - return it != supported_authentication_methods.end(); -} - -stdx::expected -ClientGreetor::client_greeting_after_tls() { - auto *socket_splicer = connection()->socket_splicer(); - auto *src_channel = socket_splicer->client_channel(); - auto *src_protocol = connection()->client_protocol(); - - auto msg_res = - ClassicFrame::recv_msg( - src_channel, src_protocol, src_protocol->server_capabilities()); - if (!msg_res) return recv_client_failed(msg_res.error()); - - auto msg = *msg_res; - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("client::greeting")); - } - - src_protocol->client_greeting(msg); - src_protocol->auth_method_name(msg.auth_method_name()); - src_protocol->client_capabilities(msg.capabilities()); - src_protocol->username(msg.username()); - src_protocol->schema(msg.schema()); - src_protocol->attributes(msg.attributes()); - - discard_current_msg(src_channel, src_protocol); - - if (!authentication_method_is_supported(msg.auth_method_name())) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("client::greeting::error")); - } - - const auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::Error>( - src_channel, src_protocol, - {CR_AUTH_PLUGIN_CANNOT_LOAD, - "Authentication method " + msg.auth_method_name() + - " is not supported", - "HY000"}); - if (!send_res) return send_client_failed(send_res.error()); - - stage(Stage::Error); - return Result::SendToClient; - } - - // fail connection from buggy clients that set the compress-cap without - // checking if the server's capabilities. - if (!client_compress_is_satisfied(src_protocol->client_capabilities(), - src_protocol->shared_capabilities())) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("client::greeting::error")); - } - const auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::server::Error>( - src_channel, src_protocol, - {ER_WRONG_COMPRESSION_ALGORITHM_CLIENT, - "Compression not supported by router."}); - if (!send_res) return send_client_failed(send_res.error()); - - stage(Stage::Error); - return Result::SendToClient; - } - - if (src_protocol->client_greeting()->auth_method_data() == "\x00"sv) { - // special value for 'empty password'. Not scrambled. - src_protocol->password(""); - - stage(Stage::Accepted); - return Result::Again; - } else if (kCapturePlaintextPassword && src_protocol->auth_method_name() == - AuthCachingSha2Password::kName) { - stage(Stage::RequestPlaintextPassword); - return Result::Again; - } else { - stage(Stage::Accepted); - return Result::Again; - } -} - -stdx::expected -ClientGreetor::request_plaintext_password() { - auto *socket_splicer = connection()->socket_splicer(); - - auto dst_channel = socket_splicer->client_channel(); - auto dst_protocol = connection()->client_protocol(); - - auto send_res = AuthCachingSha2Password::send_plaintext_password_request( - dst_channel, dst_protocol); - if (!send_res) return send_client_failed(send_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("server::auth::request::plain")); - } - - stage(Stage::PlaintextPassword); - return Result::SendToClient; -} - /** * remove trailing \0 in a string_view. * @@ -701,101 +197,6 @@ static auto strip_trailing_null(std::string_view s) { return s; } -/** - * extract the password from auth-method-data. - * - * @returns the payload without the trailing NUL-char. - * @retval false in there is no password. - */ -static std::optional password_from_auth_method_data( - std::string auth_data) { - if (auth_data.empty() || auth_data.back() != '\0') return std::nullopt; - - // strip the trailing \0 - auth_data.pop_back(); - - return auth_data; -} - -/** - * receive the client's plaintext password. - * - * after client_send_request_for_plaintext_password() - */ -stdx::expected -ClientGreetor::plaintext_password() { - auto *src_channel = connection()->socket_splicer()->client_channel(); - auto *src_protocol = connection()->client_protocol(); - - auto msg_res = ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_client_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("client::auth::plain")); - } - - if (auto pwd = password_from_auth_method_data(msg_res->value())) { - src_protocol->password(*pwd); - } - - // discard the current frame. - discard_current_msg(src_channel, src_protocol); - - stage(Stage::Accepted); - return Result::Again; -} - -stdx::expected ClientGreetor::accepted() { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("client::greeting::client_done")); - } - - auto *dst_protocol = connection()->server_protocol(); - - stage(Stage::Authenticated); - - if (dst_protocol->server_greeting().has_value()) { - // server-greeting is already present. - connection()->push_processor( - std::make_unique(connection())); - } else { - // server side requires TLS? - - auto dest_ssl_mode = connection()->dest_ssl_mode(); - auto source_ssl_mode = connection()->source_ssl_mode(); - - // if a connection is taken from the pool, make sure it matches the TLS - // requirements. - connection()->requires_tls(dest_ssl_mode == SslMode::kRequired || - dest_ssl_mode == SslMode::kPreferred || - (dest_ssl_mode == SslMode::kAsClient && - (source_ssl_mode == SslMode::kPreferred || - source_ssl_mode == SslMode::kRequired))); - - connection()->push_processor( - std::make_unique(connection(), true /* in handshake */)); - } - - return Result::Again; -} - -stdx::expected -ClientGreetor::authenticated() { - if (connection()->authenticated()) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("greeting::auth::done")); - } - stage(Stage::Ok); - } else { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("greeting::error")); - } - stage(Stage::Error); - } - return Result::Again; -} - static bool server_ssl_mode_is_satisfied( SslMode server_ssl_mode, classic_protocol::capabilities::value_type server_capabilities) { diff --git a/router/src/routing/src/classic_greeting.h b/router/src/routing/src/classic_greeting_forwarder.h similarity index 80% rename from router/src/routing/src/classic_greeting.h rename to router/src/routing/src/classic_greeting_forwarder.h index 429a8c8795ce..803fda50fbbb 100644 --- a/router/src/routing/src/classic_greeting.h +++ b/router/src/routing/src/classic_greeting_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,62 +22,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_GREETING_INCLUDED -#define ROUTING_CLASSIC_GREETING_INCLUDED +#ifndef ROUTING_CLASSIC_GREETING_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_GREETING_FORWARDER_INCLUDED #include "forwarding_processor.h" -/** - * classic protocol handshake between client<->router (and router<->server). - * - * - */ -class ClientGreetor : public ForwardingProcessor { - public: - using ForwardingProcessor::ForwardingProcessor; - - /** - * stages of the handshake flow. - */ - enum class Stage { - Init, - ServerGreeting, - ServerFirstGreeting, - ClientGreeting, - TlsAcceptInit, - TlsAccept, - ClientGreetingAfterTls, - RequestPlaintextPassword, - PlaintextPassword, - Accepted, - Authenticated, - - Error, - Ok, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - stdx::expected init(); - stdx::expected server_greeting(); - stdx::expected server_first_greeting(); - stdx::expected client_greeting(); - stdx::expected tls_accept_init(); - stdx::expected tls_accept(); - stdx::expected client_greeting_after_tls(); - stdx::expected request_plaintext_password(); - stdx::expected plaintext_password(); - stdx::expected accepted(); - stdx::expected authenticated(); - stdx::expected error(); - - Stage stage_{Stage::Init}; -}; - /** * classic protocol handshake between client<->router and router<->server. */ diff --git a/router/src/routing/src/classic_greeting_receiver.cc b/router/src/routing/src/classic_greeting_receiver.cc new file mode 100644 index 000000000000..30f324c1a1a3 --- /dev/null +++ b/router/src/routing/src/classic_greeting_receiver.cc @@ -0,0 +1,707 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_greeting_receiver.h" + +#include +#include // uniform_int_distribution +#include +#include + +#include +#include + +#include "classic_auth.h" +#include "classic_auth_caching_sha2.h" +#include "classic_auth_cleartext.h" +#include "classic_auth_forwarder.h" +#include "classic_auth_native.h" +#include "classic_auth_sha256_password.h" +#include "classic_connection_base.h" +#include "classic_frame.h" +#include "classic_greeting_forwarder.h" +#include "classic_lazy_connect.h" +#include "harness_assert.h" +#include "mysql/harness/logging/logger.h" +#include "mysql/harness/logging/logging.h" +#include "mysql/harness/net_ts/socket.h" +#include "mysql/harness/stdx/expected.h" +#include "mysql/harness/tls_error.h" +#include "mysqld_error.h" // mysql-server error-codes +#include "mysqlrouter/classic_protocol_constants.h" +#include "mysqlrouter/connection_base.h" +#include "processor.h" +#include "sql/server_component/mysql_command_services_imp.h" +#include "tracer.h" + +IMPORT_LOG_FUNCTIONS() + +using namespace std::string_literals; +using namespace std::string_view_literals; + +static constexpr const std::array supported_authentication_methods{ + AuthCachingSha2Password::kName, + AuthNativePassword::kName, + AuthCleartextPassword::kName, + AuthSha256Password::kName, +}; + +static constexpr const bool kCapturePlaintextPassword{true}; + +/** + * splice two vectors together. + * + * appends all elements of other to the vector v. + */ +template +std::vector vector_splice(std::vector v, const std::vector &other) { + v.insert(v.end(), other.begin(), other.end()); + return v; +} + +static void ssl_info_cb(const SSL *ssl, int where, int ret) { + auto *conn = reinterpret_cast( + SSL_get_app_data(ssl)); + + auto &tr = conn->tracer(); + if (!tr) return; + + if ((where & SSL_CB_LOOP) != 0) { + tr.trace( + Tracer::Event().stage("tls::state: "s + SSL_state_string_long(ssl))); + } else if ((where & SSL_CB_ALERT) != 0) { + tr.trace(Tracer::Event().stage("tls::alert: "s + + SSL_alert_type_string_long(ret) + "::"s + + SSL_alert_desc_string_long(ret))); + } else if ((where & SSL_CB_EXIT) != 0) { + if (ret == 0) { + tr.trace(Tracer::Event().stage( + "tls::state: "s + SSL_state_string_long(ssl) + " "s)); + } else if (ret < 0) { + switch (SSL_get_error(ssl, ret)) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + break; + default: + tr.trace(Tracer::Event().stage("tls"s + SSL_state_string_long(ssl) + + " "s)); + } + } + } +} + +stdx::expected ClientGreetor::process() { + switch (stage()) { + case Stage::Init: + return init(); + case Stage::ServerGreeting: + return server_greeting(); + case Stage::ServerFirstGreeting: + return server_first_greeting(); + case Stage::ClientGreeting: + return client_greeting(); + case Stage::TlsAcceptInit: + return tls_accept_init(); + case Stage::TlsAccept: + return tls_accept(); + case Stage::ClientGreetingAfterTls: + return client_greeting_after_tls(); + case Stage::RequestPlaintextPassword: + return request_plaintext_password(); + case Stage::PlaintextPassword: + return plaintext_password(); + + case Stage::Accepted: + return accepted(); + + case Stage::Authenticated: + return authenticated(); + + // the two exit-stages: + // - Error + // - Ok + case Stage::Error: + return error(); + case Stage::Ok: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected ClientGreetor::error() { + // after the greetings error has been sent to the client. + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("client::greeting::error")); + } + + auto &client_conn = connection()->socket_splicer()->client_conn(); + + (void)client_conn.cancel(); + (void)client_conn.shutdown(net::socket_base::shutdown_both); + + return Result::Done; +} + +stdx::expected ClientGreetor::init() { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("client::init")); + } + + if (!connection()->greeting_from_router()) { + stage(Stage::ServerFirstGreeting); + + connection()->push_processor( + std::make_unique(connection())); + } else { + stage(Stage::ServerGreeting); + } + return Result::Again; +} + +/** + * client<-router: server::greeting. + */ +stdx::expected +ClientGreetor::server_greeting() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->client_channel(); + auto dst_protocol = connection()->client_protocol(); + + classic_protocol::capabilities::value_type router_capabilities( + classic_protocol::capabilities::long_password | + classic_protocol::capabilities::found_rows | + classic_protocol::capabilities::long_flag | + classic_protocol::capabilities::connect_with_schema | + classic_protocol::capabilities::no_schema | + // compress (not yet) + classic_protocol::capabilities::odbc | + classic_protocol::capabilities::local_files | + // ignore_space (client only) + classic_protocol::capabilities::protocol_41 | + classic_protocol::capabilities::interactive | + // ssl (below) + // ignore sigpipe (client-only) + classic_protocol::capabilities::transactions | + classic_protocol::capabilities::secure_connection | + classic_protocol::capabilities::multi_statements | + classic_protocol::capabilities::multi_results | + classic_protocol::capabilities::ps_multi_results | + classic_protocol::capabilities::plugin_auth | + classic_protocol::capabilities::connect_attributes | + classic_protocol::capabilities::client_auth_method_data_varint | + classic_protocol::capabilities::expired_passwords | + classic_protocol::capabilities::session_track | + classic_protocol::capabilities::text_result_with_session_tracking | + classic_protocol::capabilities::optional_resultset_metadata + // compress_zstd (not yet) + ); + + if (connection()->source_ssl_mode() != SslMode::kDisabled) { + router_capabilities.set(classic_protocol::capabilities::pos::ssl); + } + + dst_protocol->server_capabilities(router_capabilities); + + auto random_auth_method_data = []() { + std::random_device rd; + std::mt19937 gen(rd()); + // Scrambles defined as 7-bit: 1..127 ... no \0 chars + std::uniform_int_distribution<> distrib(1, 127); + + std::string scramble; + scramble.resize(20 + 1); // 20 random data + [trailing, explicit \0] + + for (size_t n{}; n < scramble.size() - 1; ++n) { + scramble[n] = distrib(gen); + } + + return scramble; + }; + + auto server_greeting_version = []() { + using namespace std::string_literals; + + return MYSQL_ROUTER_VERSION "-router"s; + }; + + classic_protocol::message::server::Greeting server_greeting_msg{ + 10, // protocol + server_greeting_version(), // version + 0, // connection-id + random_auth_method_data(), // auth-method-data + dst_protocol->server_capabilities(), // server-caps + 255, // 8.0.20 sends 0xff here + classic_protocol::status::autocommit, // status-flags + std::string(AuthCachingSha2Password::kName), // auth-method-name + }; + + auto send_res = + ClassicFrame::send_msg(dst_channel, dst_protocol, server_greeting_msg, + {/* no shared caps yet */}); + if (!send_res) return send_client_failed(send_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("server::greeting")); + } + + dst_protocol->auth_method_data(server_greeting_msg.auth_method_data()); + dst_protocol->server_greeting(server_greeting_msg); + + stage(Stage::ClientGreeting); + return Result::SendToClient; +} + +/** + * client<-router: server::greeting. + */ +stdx::expected +ClientGreetor::server_first_greeting() { + auto *socket_splicer = connection()->socket_splicer(); + + // ServerFirstGreetor either + // - sent the server-greeting to the client and + // left the server connection open, or + // - sent the error to the client and + // closed the connection. + + auto &server_conn = socket_splicer->server_conn(); + + if (server_conn.is_open()) { + stage(Stage::ClientGreeting); + } else { + stage(Stage::Error); + } + + return Result::Again; +} + +static bool client_ssl_mode_is_satisfied( + SslMode client_ssl_mode, + classic_protocol::capabilities::value_type shared_capabilities) { + if ((client_ssl_mode == SslMode::kRequired) && + !shared_capabilities.test(classic_protocol::capabilities::pos::ssl)) { + return false; + } + + return true; +} + +static bool client_compress_is_satisfied( + classic_protocol::capabilities::value_type client_capabilities, + classic_protocol::capabilities::value_type shared_capabilities) { + // client enabled "zlib-compress" without checking the server's caps. + // + // fail the connect. + return !( + client_capabilities.test(classic_protocol::capabilities::pos::compress) && + !shared_capabilities.test(classic_protocol::capabilities::pos::compress)); +} + +static stdx::expected send_ssl_connection_error_msg( + Channel *dst_channel, ClassicProtocolState *dst_protocol, + const std::string &msg) { + return ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::Error>( + dst_channel, dst_protocol, {CR_SSL_CONNECTION_ERROR, msg}); +} + +/** + * handle client greeting. + */ +stdx::expected +ClientGreetor::client_greeting() { + auto *src_channel = connection()->socket_splicer()->client_channel(); + auto *src_protocol = connection()->client_protocol(); + + auto msg_res = + ClassicFrame::recv_msg( + src_channel, src_protocol, src_protocol->server_capabilities()); + if (!msg_res) return recv_client_failed(msg_res.error()); + + auto msg = std::move(*msg_res); + + if (src_protocol->seq_id() != 1) { + // client-greeting has seq-id 1 + return recv_client_failed(make_error_code(std::errc::bad_message)); + } + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("client::greeting")); + } + + src_protocol->client_greeting(msg); + src_protocol->client_capabilities(msg.capabilities()); + src_protocol->auth_method_name(msg.auth_method_name()); + src_protocol->username(msg.username()); + src_protocol->schema(msg.schema()); + src_protocol->attributes(msg.attributes()); + + if (!client_ssl_mode_is_satisfied(connection()->source_ssl_mode(), + src_protocol->shared_capabilities())) { + // config says: client->router MUST be encrypted, but client didn't set + // the SSL cap. + // + const auto send_res = send_ssl_connection_error_msg( + src_channel, src_protocol, + "SSL connection error: SSL is required from client"); + if (!send_res) return send_client_failed(send_res.error()); + + stage(Stage::Error); + return Result::SendToClient; + } + + // fail connection from buggy clients that set the compress-cap without + // checking the server's capabilities. + if (!client_compress_is_satisfied(src_protocol->client_capabilities(), + src_protocol->shared_capabilities())) { + const auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::Error>( + src_channel, src_protocol, + {ER_WRONG_COMPRESSION_ALGORITHM_CLIENT, + "Compression not supported by router."}); + if (!send_res) return send_client_failed(send_res.error()); + + stage(Stage::Error); + return Result::SendToClient; + } + + // remove the frame and message from the recv-buffer + discard_current_msg(src_channel, src_protocol); + + if (!src_protocol->shared_capabilities().test( + classic_protocol::capabilities::pos::ssl)) { + // client wants to stay with plaintext + + if (msg.auth_method_data() == "\x00"sv) { + // password is empty. + src_protocol->password(""); + } else { + const bool client_conn_is_secure = + connection()->socket_splicer()->client_conn().is_secure_transport(); + + if (client_conn_is_secure && + src_protocol->auth_method_name() == AuthCachingSha2Password::kName) { + stage(Stage::RequestPlaintextPassword); + return Result::Again; + } + } + + stage(Stage::Accepted); + return Result::Again; + } else if (connection()->source_ssl_mode() == SslMode::kPassthrough) { + stage(Stage::Accepted); + return Result::Again; + } else { + stage(Stage::TlsAcceptInit); + return Result::Again; + } +} + +stdx::expected +ClientGreetor::tls_accept_init() { + auto *socket_splicer = connection()->socket_splicer(); + auto *src_channel = socket_splicer->client_channel(); + + src_channel->is_tls(true); + + auto *ssl_ctx = connection()->context().source_ssl_ctx()->get(); + // tls <-> (any) + if (ssl_ctx == nullptr) { + // shouldn't happen. But if it does, close the connection. + log_warning("failed to create SSL_CTX"); + return recv_client_failed(make_error_code(std::errc::invalid_argument)); + } + src_channel->init_ssl(ssl_ctx); + + SSL_set_app_data(src_channel->ssl(), connection()); + SSL_set_info_callback(src_channel->ssl(), ssl_info_cb); + + stage(Stage::TlsAccept); + return Result::Again; +} + +stdx::expected ClientGreetor::tls_accept() { + auto *socket_splicer = connection()->socket_splicer(); + auto *client_channel = socket_splicer->client_channel(); + + if (!client_channel->tls_init_is_finished()) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("tls::accept")); + } + + auto res = socket_splicer->tls_accept(); + if (!res) { + const auto ec = res.error(); + + // the send-buffer contains an alert message telling the client why the + // accept failed. + if (!client_channel->send_buffer().empty()) { + if (ec != TlsErrc::kWantRead) { + log_debug("tls-accept failed: %s", ec.message().c_str()); + + stage(Stage::Error); + } + return Result::SendToClient; + } + + if (ec == TlsErrc::kWantRead) return Result::RecvFromClient; + + log_fatal_error_code("tls-accept failed", ec); + + return recv_client_failed(ec); + } + } + + if (auto &tr = tracer()) { + auto *ssl = client_channel->ssl(); + std::ostringstream oss; + oss << "tls::accept::ok: " << SSL_get_version(ssl); + oss << " using " << SSL_get_cipher_name(ssl); + + if (SSL_session_reused(ssl) != 0) { + oss << ", session_reused"; + } + + tr.trace(Tracer::Event().stage(oss.str())); + } + + stage(Stage::ClientGreetingAfterTls); + + // after tls_accept() there may still be data in the send-buffer that must + // be sent. + if (!client_channel->send_buffer().empty()) { + return Result::SendToClient; + } + // TLS is accepted, more client greeting should follow. + + return Result::Again; +} + +/** + * check if the authentication method is supported. + * + * @see supported_authentication_methods + * + * @retval true auth_method_name is supported + * @retval false auth_method_name is not supported + */ +static bool authentication_method_is_supported( + const std::string &auth_method_name) { + auto it = std::find(supported_authentication_methods.begin(), + supported_authentication_methods.end(), auth_method_name); + return it != supported_authentication_methods.end(); +} + +stdx::expected +ClientGreetor::client_greeting_after_tls() { + auto *socket_splicer = connection()->socket_splicer(); + auto *src_channel = socket_splicer->client_channel(); + auto *src_protocol = connection()->client_protocol(); + + auto msg_res = + ClassicFrame::recv_msg( + src_channel, src_protocol, src_protocol->server_capabilities()); + if (!msg_res) return recv_client_failed(msg_res.error()); + + auto msg = *msg_res; + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("client::greeting")); + } + + src_protocol->client_greeting(msg); + src_protocol->auth_method_name(msg.auth_method_name()); + src_protocol->client_capabilities(msg.capabilities()); + src_protocol->username(msg.username()); + src_protocol->schema(msg.schema()); + src_protocol->attributes(msg.attributes()); + + discard_current_msg(src_channel, src_protocol); + + if (!authentication_method_is_supported(msg.auth_method_name())) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("client::greeting::error")); + } + + const auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::Error>( + src_channel, src_protocol, + {CR_AUTH_PLUGIN_CANNOT_LOAD, + "Authentication method " + msg.auth_method_name() + + " is not supported", + "HY000"}); + if (!send_res) return send_client_failed(send_res.error()); + + stage(Stage::Error); + return Result::SendToClient; + } + + // fail connection from buggy clients that set the compress-cap without + // checking if the server's capabilities. + if (!client_compress_is_satisfied(src_protocol->client_capabilities(), + src_protocol->shared_capabilities())) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("client::greeting::error")); + } + const auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::server::Error>( + src_channel, src_protocol, + {ER_WRONG_COMPRESSION_ALGORITHM_CLIENT, + "Compression not supported by router."}); + if (!send_res) return send_client_failed(send_res.error()); + + stage(Stage::Error); + return Result::SendToClient; + } + + if (src_protocol->client_greeting()->auth_method_data() == "\x00"sv) { + // special value for 'empty password'. Not scrambled. + src_protocol->password(""); + + stage(Stage::Accepted); + return Result::Again; + } else if (kCapturePlaintextPassword && src_protocol->auth_method_name() == + AuthCachingSha2Password::kName) { + stage(Stage::RequestPlaintextPassword); + return Result::Again; + } else { + stage(Stage::Accepted); + return Result::Again; + } +} + +stdx::expected +ClientGreetor::request_plaintext_password() { + auto *socket_splicer = connection()->socket_splicer(); + + auto dst_channel = socket_splicer->client_channel(); + auto dst_protocol = connection()->client_protocol(); + + auto send_res = AuthCachingSha2Password::send_plaintext_password_request( + dst_channel, dst_protocol); + if (!send_res) return send_client_failed(send_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("server::auth::request::plain")); + } + + stage(Stage::PlaintextPassword); + return Result::SendToClient; +} + +/** + * extract the password from auth-method-data. + * + * @returns the payload without the trailing NUL-char. + * @retval false in there is no password. + */ +static std::optional password_from_auth_method_data( + std::string auth_data) { + if (auth_data.empty() || auth_data.back() != '\0') return std::nullopt; + + // strip the trailing \0 + auth_data.pop_back(); + + return auth_data; +} + +/** + * receive the client's plaintext password. + * + * after client_send_request_for_plaintext_password() + */ +stdx::expected +ClientGreetor::plaintext_password() { + auto *src_channel = connection()->socket_splicer()->client_channel(); + auto *src_protocol = connection()->client_protocol(); + + auto msg_res = ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_client_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("client::auth::plain")); + } + + if (auto pwd = password_from_auth_method_data(msg_res->value())) { + src_protocol->password(*pwd); + } + + // discard the current frame. + discard_current_msg(src_channel, src_protocol); + + stage(Stage::Accepted); + return Result::Again; +} + +stdx::expected ClientGreetor::accepted() { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("client::greeting::client_done")); + } + + auto *dst_protocol = connection()->server_protocol(); + + stage(Stage::Authenticated); + + if (dst_protocol->server_greeting().has_value()) { + // server-greeting is already present. + connection()->push_processor( + std::make_unique(connection())); + } else { + // server side requires TLS? + + auto dest_ssl_mode = connection()->dest_ssl_mode(); + auto source_ssl_mode = connection()->source_ssl_mode(); + + // if a connection is taken from the pool, make sure it matches the TLS + // requirements. + connection()->requires_tls(dest_ssl_mode == SslMode::kRequired || + dest_ssl_mode == SslMode::kPreferred || + (dest_ssl_mode == SslMode::kAsClient && + (source_ssl_mode == SslMode::kPreferred || + source_ssl_mode == SslMode::kRequired))); + + connection()->push_processor( + std::make_unique(connection(), true /* in handshake */)); + } + + return Result::Again; +} + +stdx::expected +ClientGreetor::authenticated() { + if (connection()->authenticated()) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("greeting::auth::done")); + } + stage(Stage::Ok); + } else { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("greeting::error")); + } + stage(Stage::Error); + } + return Result::Again; +} diff --git a/router/src/routing/src/classic_greeting_receiver.h b/router/src/routing/src/classic_greeting_receiver.h new file mode 100644 index 000000000000..b5dfd0273efa --- /dev/null +++ b/router/src/routing/src/classic_greeting_receiver.h @@ -0,0 +1,81 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_GREETING_RECEIVER_INCLUDED +#define ROUTING_CLASSIC_GREETING_RECEIVER_INCLUDED + +#include "processor.h" + +/** + * classic protocol handshake between client<->router (and router<->server). + * + * + */ +class ClientGreetor : public Processor { + public: + using Processor::Processor; + + /** + * stages of the handshake flow. + */ + enum class Stage { + Init, + ServerGreeting, + ServerFirstGreeting, + ClientGreeting, + TlsAcceptInit, + TlsAccept, + ClientGreetingAfterTls, + RequestPlaintextPassword, + PlaintextPassword, + Accepted, + Authenticated, + + Error, + Ok, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + [[nodiscard]] Stage stage() const { return stage_; } + + private: + stdx::expected init(); + stdx::expected server_greeting(); + stdx::expected server_first_greeting(); + stdx::expected client_greeting(); + stdx::expected tls_accept_init(); + stdx::expected tls_accept(); + stdx::expected client_greeting_after_tls(); + stdx::expected request_plaintext_password(); + stdx::expected plaintext_password(); + stdx::expected accepted(); + stdx::expected authenticated(); + stdx::expected error(); + + Stage stage_{Stage::Init}; +}; + +#endif diff --git a/router/src/routing/src/classic_init_schema_forwarder.cc b/router/src/routing/src/classic_init_schema_forwarder.cc new file mode 100644 index 000000000000..4578365cd7a6 --- /dev/null +++ b/router/src/routing/src/classic_init_schema_forwarder.cc @@ -0,0 +1,193 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_init_schema_forwarder.h" + +#include "classic_connection_base.h" +#include "classic_forwarder.h" +#include "classic_frame.h" +#include "classic_lazy_connect.h" +#include "mysql/harness/stdx/expected.h" +#include "mysql/harness/tls_error.h" +#include "tracer.h" + +// init-schema + +stdx::expected +InitSchemaForwarder::process() { + switch (stage()) { + case Stage::Command: + return command(); + case Stage::Connect: + return connect(); + case Stage::Connected: + return connected(); + case Stage::Response: + return response(); + case Stage::Ok: + return ok(); + case Stage::Error: + return error(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected +InitSchemaForwarder::command() { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("init_schema::command")); + } + + auto &server_conn = connection()->socket_splicer()->server_conn(); + if (!server_conn.is_open()) { + stage(Stage::Connect); + return Result::Again; + } else { + stage(Stage::Response); + return forward_client_to_server(); + } +} + +stdx::expected +InitSchemaForwarder::connect() { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("init_schema::connect")); + } + + stage(Stage::Connected); + + connection()->push_processor( + std::make_unique(connection(), false /* in-handshake */)); + + return Result::Again; +} + +stdx::expected +InitSchemaForwarder::connected() { + auto &server_conn = connection()->socket_splicer()->server_conn(); + if (!server_conn.is_open()) { + // Connector sent an server::Error already. + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->client_channel(); + auto src_protocol = connection()->client_protocol(); + + // take the client::command from the connection. + auto recv_res = + ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); + if (!recv_res) return recv_client_failed(recv_res.error()); + + discard_current_msg(src_channel, src_protocol); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("init_schema::error")); + } + + stage(Stage::Done); + return Result::Again; + } + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("init_schema::connected")); + } + + stage(Stage::Response); + return forward_client_to_server(); +} + +stdx::expected +InitSchemaForwarder::response() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Error = ClassicFrame::cmd_byte(), + Ok = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("init_schema::response")); + } + + return stdx::make_unexpected(make_error_code(std::errc::bad_message)); +} + +stdx::expected InitSchemaForwarder::ok() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + // Ok packet may have session trackers. + auto msg_res = + ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("init_schema::ok")); + } + + auto msg = *msg_res; + + if (!msg.session_changes().empty()) { + // ignore the "some_state_changed" which would make the connection not + // sharable even though we can nicely recover. + auto track_res = connection()->track_session_changes( + net::buffer(msg.session_changes()), src_protocol->shared_capabilities(), + true /* ignore some_state_changed */); + } + + stage(Stage::Done); + + return forward_server_to_client(); +} + +stdx::expected +InitSchemaForwarder::error() { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("init_schema::error")); + } + + stage(Stage::Done); + + return forward_server_to_client(); +} diff --git a/router/src/routing/src/classic_init_schema_forwarder.h b/router/src/routing/src/classic_init_schema_forwarder.h new file mode 100644 index 000000000000..4126f982f555 --- /dev/null +++ b/router/src/routing/src/classic_init_schema_forwarder.h @@ -0,0 +1,60 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_INIT_SCHEMA_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_INIT_SCHEMA_FORWARDER_INCLUDED + +#include "forwarding_processor.h" + +class InitSchemaForwarder : public ForwardingProcessor { + public: + using ForwardingProcessor::ForwardingProcessor; + + enum class Stage { + Command, + Connect, + Connected, + Response, + Ok, + Error, + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + Stage stage() const { return stage_; } + + private: + stdx::expected command(); + stdx::expected connect(); + stdx::expected connected(); + stdx::expected response(); + stdx::expected ok(); + stdx::expected error(); + + Stage stage_{Stage::Command}; +}; + +#endif diff --git a/router/src/routing/src/classic_init_schema.cc b/router/src/routing/src/classic_init_schema_sender.cc similarity index 51% rename from router/src/routing/src/classic_init_schema.cc rename to router/src/routing/src/classic_init_schema_sender.cc index b99ebbb1e32c..2f5c9a046142 100644 --- a/router/src/routing/src/classic_init_schema.cc +++ b/router/src/routing/src/classic_init_schema_sender.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,176 +22,16 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_init_schema.h" +#include "classic_init_schema_sender.h" #include "classic_connection_base.h" -#include "classic_forwarder.h" #include "classic_frame.h" -#include "classic_lazy_connect.h" #include "mysql/harness/stdx/expected.h" #include "mysql/harness/tls_error.h" #include "tracer.h" // init-schema -stdx::expected -InitSchemaForwarder::process() { - switch (stage()) { - case Stage::Command: - return command(); - case Stage::Connect: - return connect(); - case Stage::Connected: - return connected(); - case Stage::Response: - return response(); - case Stage::Ok: - return ok(); - case Stage::Error: - return error(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected -InitSchemaForwarder::command() { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("init_schema::command")); - } - - auto &server_conn = connection()->socket_splicer()->server_conn(); - if (!server_conn.is_open()) { - stage(Stage::Connect); - return Result::Again; - } else { - stage(Stage::Response); - return forward_client_to_server(); - } -} - -stdx::expected -InitSchemaForwarder::connect() { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("init_schema::connect")); - } - - stage(Stage::Connected); - - connection()->push_processor( - std::make_unique(connection(), false /* in-handshake */)); - - return Result::Again; -} - -stdx::expected -InitSchemaForwarder::connected() { - auto &server_conn = connection()->socket_splicer()->server_conn(); - if (!server_conn.is_open()) { - // Connector sent an server::Error already. - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->client_channel(); - auto src_protocol = connection()->client_protocol(); - - // take the client::command from the connection. - auto recv_res = - ClassicFrame::ensure_has_full_frame(src_channel, src_protocol); - if (!recv_res) return recv_client_failed(recv_res.error()); - - discard_current_msg(src_channel, src_protocol); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("init_schema::error")); - } - - stage(Stage::Done); - return Result::Again; - } - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("init_schema::connected")); - } - - stage(Stage::Response); - return forward_client_to_server(); -} - -stdx::expected -InitSchemaForwarder::response() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Error = ClassicFrame::cmd_byte(), - Ok = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("init_schema::response")); - } - - return stdx::make_unexpected(make_error_code(std::errc::bad_message)); -} - -stdx::expected InitSchemaForwarder::ok() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - // Ok packet may have session trackers. - auto msg_res = - ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("init_schema::ok")); - } - - auto msg = *msg_res; - - if (!msg.session_changes().empty()) { - // ignore the "some_state_changed" which would make the connection not - // sharable even though we can nicely recover. - auto track_res = connection()->track_session_changes( - net::buffer(msg.session_changes()), src_protocol->shared_capabilities(), - true /* ignore some_state_changed */); - } - - stage(Stage::Done); - - return forward_server_to_client(); -} - -stdx::expected -InitSchemaForwarder::error() { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("init_schema::error")); - } - - stage(Stage::Done); - - return forward_server_to_client(); -} - stdx::expected InitSchemaSender::process() { switch (stage()) { case Stage::Command: diff --git a/router/src/routing/src/classic_init_schema_sender.h b/router/src/routing/src/classic_init_schema_sender.h new file mode 100644 index 000000000000..832b05574c82 --- /dev/null +++ b/router/src/routing/src/classic_init_schema_sender.h @@ -0,0 +1,59 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_INIT_SCHEMA_SENDER_INCLUDED +#define ROUTING_CLASSIC_INIT_SCHEMA_SENDER_INCLUDED + +#include "processor.h" + +class InitSchemaSender : public Processor { + public: + InitSchemaSender(MysqlRoutingClassicConnectionBase *conn, std::string schema) + : Processor(conn), schema_{std::move(schema)} {} + + enum class Stage { + Command, + Response, + Ok, + Error, + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + Stage stage() const { return stage_; } + + private: + stdx::expected command(); + stdx::expected response(); + stdx::expected ok(); + stdx::expected error(); + + Stage stage_{Stage::Command}; + + std::string schema_; +}; + +#endif diff --git a/router/src/routing/src/classic_kill.cc b/router/src/routing/src/classic_kill_forwarder.cc similarity index 98% rename from router/src/routing/src/classic_kill.cc rename to router/src/routing/src/classic_kill_forwarder.cc index 53c67f3e6010..5207913fd532 100644 --- a/router/src/routing/src/classic_kill.cc +++ b/router/src/routing/src/classic_kill_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_kill.h" +#include "classic_kill_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_kill.h b/router/src/routing/src/classic_kill_forwarder.h similarity index 92% rename from router/src/routing/src/classic_kill.h rename to router/src/routing/src/classic_kill_forwarder.h index 74ef33d221e8..f255fe7b78d7 100644 --- a/router/src/routing/src/classic_kill.h +++ b/router/src/routing/src/classic_kill_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_KILL_INCLUDED -#define ROUTING_CLASSIC_KILL_INCLUDED +#ifndef ROUTING_CLASSIC_KILL_FORWADER_INCLUDED +#define ROUTING_CLASSIC_KILL_FORWADER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_lazy_connect.cc b/router/src/routing/src/classic_lazy_connect.cc index 016cfd5d52e2..b45dbfdcd4ca 100644 --- a/router/src/routing/src/classic_lazy_connect.cc +++ b/router/src/routing/src/classic_lazy_connect.cc @@ -24,16 +24,13 @@ #include "classic_lazy_connect.h" -#include "classic_change_user.h" +#include "classic_change_user_sender.h" #include "classic_connect.h" #include "classic_connection_base.h" -#include "classic_greeting.h" -#include "classic_init_schema.h" -#include "classic_query.h" -#include "classic_reset_connection.h" -#include "hexify.h" - -using mysql_harness::hexify; +#include "classic_greeting_forwarder.h" // ServerGreetor +#include "classic_init_schema_sender.h" +#include "classic_query_sender.h" +#include "classic_reset_connection_sender.h" stdx::expected LazyConnector::process() { switch (stage()) { diff --git a/router/src/routing/src/classic_list_fields.cc b/router/src/routing/src/classic_list_fields_forwarder.cc similarity index 98% rename from router/src/routing/src/classic_list_fields.cc rename to router/src/routing/src/classic_list_fields_forwarder.cc index 7105b25321db..1a3111e26cc2 100644 --- a/router/src/routing/src/classic_list_fields.cc +++ b/router/src/routing/src/classic_list_fields_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_list_fields.h" +#include "classic_list_fields_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_list_fields.h b/router/src/routing/src/classic_list_fields_forwarder.h similarity index 91% rename from router/src/routing/src/classic_list_fields.h rename to router/src/routing/src/classic_list_fields_forwarder.h index 67457b39e99a..08266a241b6c 100644 --- a/router/src/routing/src/classic_list_fields.h +++ b/router/src/routing/src/classic_list_fields_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_LIST_FIELDS_INCLUDED -#define ROUTING_CLASSIC_LIST_FIELDS_INCLUDED +#ifndef ROUTING_CLASSIC_LIST_FIELDS_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_LIST_FIELDS_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_ping.cc b/router/src/routing/src/classic_ping_forwarder.cc similarity index 98% rename from router/src/routing/src/classic_ping.cc rename to router/src/routing/src/classic_ping_forwarder.cc index d1677b8af8f7..dfe0b570e320 100644 --- a/router/src/routing/src/classic_ping.cc +++ b/router/src/routing/src/classic_ping_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_ping.h" +#include "classic_ping_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_ping.h b/router/src/routing/src/classic_ping_forwarder.h similarity index 92% rename from router/src/routing/src/classic_ping.h rename to router/src/routing/src/classic_ping_forwarder.h index 2e13937896a3..0ae58e0c0212 100644 --- a/router/src/routing/src/classic_ping.h +++ b/router/src/routing/src/classic_ping_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_PING_INCLUDED -#define ROUTING_CLASSIC_PING_INCLUDED +#ifndef ROUTING_CLASSIC_PING_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_PING_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_query.cc b/router/src/routing/src/classic_query_forwarder.cc similarity index 78% rename from router/src/routing/src/classic_query.cc rename to router/src/routing/src/classic_query_forwarder.cc index 1429bb70991d..2e1ef7213e29 100644 --- a/router/src/routing/src/classic_query.cc +++ b/router/src/routing/src/classic_query_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_query.h" +#include "classic_query_forwarder.h" #include #include @@ -1263,362 +1263,3 @@ stdx::expected QueryForwarder::error() { stage(Stage::Done); return forward_server_to_client(); } - -// Sender - -stdx::expected QuerySender::process() { - switch (stage()) { - case Stage::Command: - return command(); - case Stage::Response: - return response(); - case Stage::ColumnCount: - return column_count(); - case Stage::LoadData: - return load_data(); - case Stage::Data: - return data(); - case Stage::Column: - return column(); - case Stage::ColumnEnd: - return column_end(); - case Stage::RowOrEnd: - return row_or_end(); - case Stage::Row: - return row(); - case Stage::RowEnd: - return row_end(); - case Stage::Ok: - return ok(); - case Stage::Error: - return error(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected QuerySender::command() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::command")); - tr.trace(Tracer::Event().stage(">> " + stmt_)); - } - - dst_protocol->seq_id(0xff); - - auto send_res = ClassicFrame::send_msg( - dst_channel, dst_protocol, - classic_protocol::borrowed::message::client::Query{stmt_}); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - - return Result::SendToServer; -} - -stdx::expected QuerySender::response() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Error = ClassicFrame::cmd_byte(), - Ok = ClassicFrame::cmd_byte(), - LoadData = 0xfb, - }; - - switch (Msg{msg_type}) { - case Msg::Error: - stage(Stage::Error); - return Result::Again; - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - case Msg::LoadData: - stage(Stage::LoadData); - return Result::Again; - } - - stage(Stage::ColumnCount); - return Result::Again; -} - -stdx::expected QuerySender::load_data() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto msg_res = - ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::load_data")); - } - - // we could decode the filename here. - - discard_current_msg(src_channel, src_protocol); - - stage(Stage::Data); - return Result::Again; -} - -stdx::expected QuerySender::data() { - auto *socket_splicer = connection()->socket_splicer(); - auto dst_channel = socket_splicer->server_channel(); - auto dst_protocol = connection()->server_protocol(); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::data")); - } - - // an empty packet. - auto send_res = - ClassicFrame::send_msg( - dst_channel, dst_protocol, {}); - if (!send_res) return send_server_failed(send_res.error()); - - return Result::SendToServer; -} - -stdx::expected QuerySender::column_count() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto msg_res = ClassicFrame::recv_msg< - classic_protocol::borrowed::message::server::ColumnCount>(src_channel, - src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::column_count")); - } - - if (handler_) handler_->on_column_count(msg_res->count()); - - columns_left_ = msg_res->count(); - - discard_current_msg(src_channel, src_protocol); - - stage(Stage::Column); - return Result::Again; -} - -stdx::expected QuerySender::column() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto msg_res = - ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::column")); - } - - discard_current_msg(src_channel, src_protocol); - - if (handler_) handler_->on_column(*msg_res); - - if (--columns_left_ == 0) { - const auto skips_eof_pos = - classic_protocol::capabilities::pos::text_result_with_session_tracking; - - const bool server_skips_end_of_columns{ - src_protocol->shared_capabilities().test(skips_eof_pos)}; - - if (server_skips_end_of_columns) { - // next is a Row, not a EOF packet. - stage(Stage::RowOrEnd); - } else { - stage(Stage::ColumnEnd); - } - } - - return Result::Again; -} - -stdx::expected QuerySender::column_end() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto msg_res = - ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::column_end")); - } - - discard_current_msg(src_channel, src_protocol); - - stage(Stage::RowOrEnd); - return Result::Again; -} - -stdx::expected QuerySender::row_or_end() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Error = ClassicFrame::cmd_byte(), - EndOfResult = - ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::EndOfResult: - stage(Stage::RowEnd); - return Result::Again; - case Msg::Error: - stage(Stage::Error); - return Result::Again; - } - - stage(Stage::Row); - return Result::Again; -} - -stdx::expected QuerySender::row() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto msg_res = ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::row")); - } - - discard_current_msg(src_channel, src_protocol); - - if (handler_) handler_->on_row(*msg_res); - - stage(Stage::RowOrEnd); - return Result::Again; -} - -stdx::expected QuerySender::row_end() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto msg_res = ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - auto eof_msg = std::move(*msg_res); - - if (handler_) handler_->on_row_end(eof_msg); - - if (!eof_msg.session_changes().empty()) { - auto track_res = connection()->track_session_changes( - net::buffer(eof_msg.session_changes()), - src_protocol->shared_capabilities()); - } - - discard_current_msg(src_channel, src_protocol); - - if (eof_msg.status_flags().test( - classic_protocol::status::pos::more_results_exist)) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::more_resultsets")); - } - stage(Stage::Response); - - return Result::Again; - } else { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::row_end")); - } - stage(Stage::Done); - return Result::Again; - } -} - -stdx::expected QuerySender::ok() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto msg_res = ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - discard_current_msg(src_channel, src_protocol); - - auto msg = std::move(*msg_res); - - if (handler_) handler_->on_ok(msg); - - if (!msg.session_changes().empty()) { - auto changes_state = classify(stmt_, false); - - auto track_res = connection()->track_session_changes( - net::buffer(msg.session_changes()), src_protocol->shared_capabilities(), - changes_state & StmtClassifier::NoStateChangeIgnoreTracker); - } - - if (msg.status_flags().test( - classic_protocol::status::pos::more_results_exist)) { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::ok::more")); - } - stage(Stage::Response); - } else { - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::ok::done")); - } - stage(Stage::Done); - } - return Result::Again; -} - -stdx::expected QuerySender::error() { - auto *socket_splicer = connection()->socket_splicer(); - auto src_channel = socket_splicer->server_channel(); - auto src_protocol = connection()->server_protocol(); - - auto msg_res = - ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("query::error")); - } - - discard_current_msg(src_channel, src_protocol); - - if (handler_) handler_->on_error(*msg_res); - - stage(Stage::Done); - return Result::Again; -} diff --git a/router/src/routing/src/classic_init_schema.h b/router/src/routing/src/classic_query_forwarder.h similarity index 61% rename from router/src/routing/src/classic_init_schema.h rename to router/src/routing/src/classic_query_forwarder.h index f2adfdb7ad26..44e7bdc64dce 100644 --- a/router/src/routing/src/classic_init_schema.h +++ b/router/src/routing/src/classic_query_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,68 +22,73 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_INIT_SCHEMA_INCLUDED -#define ROUTING_CLASSIC_INIT_SCHEMA_INCLUDED +#ifndef ROUTING_CLASSIC_QUERY_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_QUERY_FORWARDER_INCLUDED + +#include #include "forwarding_processor.h" +#include "stmt_classifier.h" -class InitSchemaForwarder : public ForwardingProcessor { +class QueryForwarder : public ForwardingProcessor { public: using ForwardingProcessor::ForwardingProcessor; enum class Stage { Command, + Connect, Connected, + Response, + ColumnCount, + Column, + ColumnEnd, + RowOrEnd, + Row, + RowEnd, + + LoadData, + Data, + Ok, Error, + Done, }; stdx::expected process() override; void stage(Stage stage) { stage_ = stage; } - Stage stage() const { return stage_; } + [[nodiscard]] Stage stage() const { return stage_; } private: stdx::expected command(); stdx::expected connect(); stdx::expected connected(); stdx::expected response(); - stdx::expected ok(); - stdx::expected error(); + stdx::expected load_data(); + stdx::expected data(); - Stage stage_{Stage::Command}; -}; - -class InitSchemaSender : public Processor { - public: - InitSchemaSender(MysqlRoutingClassicConnectionBase *conn, std::string schema) - : Processor(conn), schema_{std::move(schema)} {} + stdx::expected column_count(); + stdx::expected column(); + stdx::expected column_end(); + stdx::expected row_or_end(); + stdx::expected row(); + stdx::expected row_end(); - enum class Stage { - Command, - Response, - Ok, - Error, - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - Stage stage() const { return stage_; } - - private: - stdx::expected command(); - stdx::expected response(); stdx::expected ok(); stdx::expected error(); + stdx::expected track_session_changes( + net::const_buffer session_trackers, + classic_protocol::capabilities::value_type caps); + + stdx::flags stmt_classified_{}; + Stage stage_{Stage::Command}; - std::string schema_; + uint64_t columns_left_{0}; }; #endif diff --git a/router/src/routing/src/classic_query_sender.cc b/router/src/routing/src/classic_query_sender.cc new file mode 100644 index 000000000000..f265d6a56a31 --- /dev/null +++ b/router/src/routing/src/classic_query_sender.cc @@ -0,0 +1,628 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_query_sender.h" + +#include +#include +#include +#include +#include + +#include "classic_connection_base.h" +#include "classic_forwarder.h" +#include "classic_frame.h" +#include "classic_lazy_connect.h" +#include "harness_assert.h" +#include "mysql/harness/stdx/expected.h" +#include "mysql/harness/tls_error.h" +#include "mysqld_error.h" // mysql errors +#include "mysqlrouter/classic_protocol_message.h" +#include "mysqlrouter/utils.h" // to_string +#include "processor.h" +#include "sql/lex.h" +#include "sql_exec_context.h" +#include "sql_lexer.h" +#include "sql_lexer_thd.h" + +#undef DEBUG_DUMP_TOKENS + +#ifdef DEBUG_DUMP_TOKENS +static void dump_token(SqlLexer::iterator::Token tkn) { + std::map syms; + + for (size_t ndx{}; ndx < sizeof(symbols) / sizeof(symbols[0]); ++ndx) { + auto sym = symbols[ndx]; + syms.emplace(sym.tok, std::string_view{sym.name, sym.length}); + } + std::cerr << "<" << tkn.id << ">\t| "; + + auto it = syms.find(tkn.id); + if (it != syms.end()) { + std::cerr << "sym[" << std::quoted(it->second) << "]"; + } else if (tkn.id >= 32 && tkn.id < 127) { + std::cerr << (char)tkn.id; + } else if (tkn.id == IDENT || tkn.id == IDENT_QUOTED) { + std::cerr << std::quoted(tkn.text, '`'); + } else if (tkn.id == TEXT_STRING) { + std::cerr << std::quoted(tkn.text); + } else if (tkn.id == NUM) { + std::cerr << tkn.text; + } else if (tkn.id == END_OF_INPUT) { + std::cerr << ""; + } + std::cerr << "\n"; +} +#endif + +/* + * classify statements about their behaviour with the session-tracker. + * + * Statements may + * + * - set user vars, but not set the session-tracker like: + * + * @code + * SELECT 1 INTO @a + * @endcode + * + * - create global locks, but not set the session-tracker like: + * + * @code + * LOCK INSTANCE FOR BACKUP + * FLUSH TABLES WITH READ LOCK + * @endcode + */ +static stdx::flags classify(const std::string &stmt, + bool forbid_set_trackers) { + stdx::flags classified{}; + + MEM_ROOT mem_root; + THD session; + session.mem_root = &mem_root; + + { + Parser_state parser_state; + parser_state.init(&session, stmt.data(), stmt.size()); + session.m_parser_state = &parser_state; + SqlLexer lexer(&session); + + auto lexer_it = lexer.begin(); + if (lexer_it != lexer.end()) { + auto first = *lexer_it; + auto last = first; +#ifdef DEBUG_DUMP_TOKENS + dump_token(first); +#endif + + ++lexer_it; + + for (; lexer_it != lexer.end(); ++lexer_it) { + auto tkn = *lexer_it; + +#ifdef DEBUG_DUMP_TOKENS + dump_token(tkn); +#endif + + if (first.id == SELECT_SYM) { + if (tkn.id == SQL_CALC_FOUND_ROWS) { + classified |= StmtClassifier::StateChangeOnSuccess; + classified |= StmtClassifier::StateChangeOnError; + } + + // SELECT ... INTO ... + if (tkn.id == INTO) { + classified |= StmtClassifier::StateChangeOnSuccess; + } + } else if (first.id == LOCK_SYM) { + // match: LOCK INSTANCE FOR BACKUP + // but not: LOCK TABLES ... + if (tkn.id == INSTANCE_SYM) { + classified |= StmtClassifier::StateChangeOnSuccess; + } + } else if (first.id == FLUSH_SYM) { + // match: FLUSH TABLES WITH ... + // but not: FLUSH TABLES t1 WITH ... + if (last.id == TABLES && tkn.id == WITH) { + classified |= StmtClassifier::StateChangeOnSuccess; + } + } else if (first.id == GET_SYM && tkn.id == DIAGNOSTICS_SYM) { + // GET [CURRENT] DIAGNOSTICS ... + classified |= StmtClassifier::ForbiddenFunctionWithConnSharing; + } + + // check forbidden functions in DML statements: + // + // can appear more or less everywhere: + // + // - INSERT INTO tlb VALUES (GET_LOCK("abc", 1)) + // - SELECT GET_LOCK("abc", 1) + // - SELECT * FROM tbl WHERE GET_LOCK(...) + // - CALL FOO(GET_LOCK(...)) + // - DO GET_LOCK() + // + // It is ok, if it appears in: + // + // - DDL like CREATE|DROP|ALTER + + switch (first.id) { + case SELECT_SYM: + case INSERT_SYM: + case UPDATE_SYM: + case DELETE_SYM: + case DO_SYM: + case CALL_SYM: + case SET_SYM: + if (tkn.id == '(' && + (last.id == IDENT || last.id == IDENT_QUOTED)) { + std::string ident; + ident.resize(last.text.size()); + + // ascii-upper-case + std::transform( + last.text.begin(), last.text.end(), ident.begin(), + [](auto c) { return (c >= 'a' && c <= 'z') ? c - 0x20 : c; }); + + if (ident == "GET_LOCK" || // + ident == "SERVICE_GET_WRITE_LOCKS" || + ident == "SERVICE_GET_READ_LOCKS" || + ident == "VERSION_TOKENS_LOCK_SHARED" || + ident == "VERSION_TOKENS_LOCK_EXCLUSIVE") { + classified |= StmtClassifier::StateChangeOnSuccess; + } + + if (ident == "LAST_INSERT_ID") { + classified |= StmtClassifier::ForbiddenFunctionWithConnSharing; + } + } + + break; + } + + if (first.id == SET_SYM) { + if (tkn.id == SET_VAR || tkn.id == EQ) { + if (last.id == LEX_HOSTNAME) { + // LEX_HOSTNAME: @IDENT -> user-var + // SET_VAR : := + // EQ : = + + classified |= StmtClassifier::StateChangeOnSuccess; + classified |= StmtClassifier::StateChangeOnError; + } else if ((last.id == IDENT || last.id == IDENT_QUOTED)) { + // SET .* session_track_gtids := ... + // ^^ or = + // ^^ or quoted with backticks + // + // forbids also + // + // - SET SESSION (ident|ident_quoted) + // - SET @@SESSION.(ident|ident_quoted) + // - SET LOCAL (ident|ident_quoted) + // - SET @@LOCAL.(ident|ident_quoted) + + std::string ident; + ident.resize(last.text.size()); + + // ascii-upper-case + std::transform( + last.text.begin(), last.text.end(), ident.begin(), + [](auto c) { return (c >= 'a' && c <= 'z') ? c - 0x20 : c; }); + + if (ident == "SESSION_TRACK_GTIDS" || // + ident == "SESSION_TRACK_TRANSACTION_INFO" || + ident == "SESSION_TRACK_STATE_CHANGE" || + ident == "SESSION_TRACK_SYSTEM_VARIABLES") { + if (forbid_set_trackers) { + classified |= StmtClassifier::ForbiddenSetWithConnSharing; + } + } + } + } + } else { + if (last.id == LEX_HOSTNAME && tkn.id == SET_VAR) { // := + classified |= StmtClassifier::StateChangeOnSuccess; + classified |= StmtClassifier::StateChangeOnError; + } + } + last = tkn; + } + + if (first.id == SET_SYM) { + if (!classified) { + return StmtClassifier::NoStateChangeIgnoreTracker; + } else { + return classified; + } + } else { + if (!classified) { + return StmtClassifier::StateChangeOnTracker; + } else { + return classified; + } + } + } + } + + // unknown or empty statement. + return StmtClassifier::StateChangeOnTracker; +} + +// Sender + +stdx::expected QuerySender::process() { + switch (stage()) { + case Stage::Command: + return command(); + case Stage::Response: + return response(); + case Stage::ColumnCount: + return column_count(); + case Stage::LoadData: + return load_data(); + case Stage::Data: + return data(); + case Stage::Column: + return column(); + case Stage::ColumnEnd: + return column_end(); + case Stage::RowOrEnd: + return row_or_end(); + case Stage::Row: + return row(); + case Stage::RowEnd: + return row_end(); + case Stage::Ok: + return ok(); + case Stage::Error: + return error(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected QuerySender::command() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::command")); + tr.trace(Tracer::Event().stage(">> " + stmt_)); + } + + dst_protocol->seq_id(0xff); + + auto send_res = ClassicFrame::send_msg( + dst_channel, dst_protocol, + classic_protocol::borrowed::message::client::Query{stmt_}); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + + return Result::SendToServer; +} + +stdx::expected QuerySender::response() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Error = ClassicFrame::cmd_byte(), + Ok = ClassicFrame::cmd_byte(), + LoadData = 0xfb, + }; + + switch (Msg{msg_type}) { + case Msg::Error: + stage(Stage::Error); + return Result::Again; + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + case Msg::LoadData: + stage(Stage::LoadData); + return Result::Again; + } + + stage(Stage::ColumnCount); + return Result::Again; +} + +stdx::expected QuerySender::load_data() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto msg_res = + ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::load_data")); + } + + // we could decode the filename here. + + discard_current_msg(src_channel, src_protocol); + + stage(Stage::Data); + return Result::Again; +} + +stdx::expected QuerySender::data() { + auto *socket_splicer = connection()->socket_splicer(); + auto dst_channel = socket_splicer->server_channel(); + auto dst_protocol = connection()->server_protocol(); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::data")); + } + + // an empty packet. + auto send_res = + ClassicFrame::send_msg( + dst_channel, dst_protocol, {}); + if (!send_res) return send_server_failed(send_res.error()); + + return Result::SendToServer; +} + +stdx::expected QuerySender::column_count() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto msg_res = ClassicFrame::recv_msg< + classic_protocol::borrowed::message::server::ColumnCount>(src_channel, + src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::column_count")); + } + + if (handler_) handler_->on_column_count(msg_res->count()); + + columns_left_ = msg_res->count(); + + discard_current_msg(src_channel, src_protocol); + + stage(Stage::Column); + return Result::Again; +} + +stdx::expected QuerySender::column() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto msg_res = + ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::column")); + } + + discard_current_msg(src_channel, src_protocol); + + if (handler_) handler_->on_column(*msg_res); + + if (--columns_left_ == 0) { + const auto skips_eof_pos = + classic_protocol::capabilities::pos::text_result_with_session_tracking; + + const bool server_skips_end_of_columns{ + src_protocol->shared_capabilities().test(skips_eof_pos)}; + + if (server_skips_end_of_columns) { + // next is a Row, not a EOF packet. + stage(Stage::RowOrEnd); + } else { + stage(Stage::ColumnEnd); + } + } + + return Result::Again; +} + +stdx::expected QuerySender::column_end() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto msg_res = + ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::column_end")); + } + + discard_current_msg(src_channel, src_protocol); + + stage(Stage::RowOrEnd); + return Result::Again; +} + +stdx::expected QuerySender::row_or_end() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Error = ClassicFrame::cmd_byte(), + EndOfResult = + ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::EndOfResult: + stage(Stage::RowEnd); + return Result::Again; + case Msg::Error: + stage(Stage::Error); + return Result::Again; + } + + stage(Stage::Row); + return Result::Again; +} + +stdx::expected QuerySender::row() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto msg_res = ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::row")); + } + + discard_current_msg(src_channel, src_protocol); + + if (handler_) handler_->on_row(*msg_res); + + stage(Stage::RowOrEnd); + return Result::Again; +} + +stdx::expected QuerySender::row_end() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto msg_res = ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + auto eof_msg = std::move(*msg_res); + + if (handler_) handler_->on_row_end(eof_msg); + + if (!eof_msg.session_changes().empty()) { + auto track_res = connection()->track_session_changes( + net::buffer(eof_msg.session_changes()), + src_protocol->shared_capabilities()); + } + + discard_current_msg(src_channel, src_protocol); + + if (eof_msg.status_flags().test( + classic_protocol::status::pos::more_results_exist)) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::more_resultsets")); + } + stage(Stage::Response); + + return Result::Again; + } else { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::row_end")); + } + stage(Stage::Done); + return Result::Again; + } +} + +stdx::expected QuerySender::ok() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto msg_res = ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + discard_current_msg(src_channel, src_protocol); + + auto msg = std::move(*msg_res); + + if (handler_) handler_->on_ok(msg); + + if (!msg.session_changes().empty()) { + auto changes_state = classify(stmt_, false); + + auto track_res = connection()->track_session_changes( + net::buffer(msg.session_changes()), src_protocol->shared_capabilities(), + changes_state & StmtClassifier::NoStateChangeIgnoreTracker); + } + + if (msg.status_flags().test( + classic_protocol::status::pos::more_results_exist)) { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::ok::more")); + } + stage(Stage::Response); + } else { + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::ok::done")); + } + stage(Stage::Done); + } + return Result::Again; +} + +stdx::expected QuerySender::error() { + auto *socket_splicer = connection()->socket_splicer(); + auto src_channel = socket_splicer->server_channel(); + auto src_protocol = connection()->server_protocol(); + + auto msg_res = + ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("query::error")); + } + + discard_current_msg(src_channel, src_protocol); + + if (handler_) handler_->on_error(*msg_res); + + stage(Stage::Done); + return Result::Again; +} diff --git a/router/src/routing/src/classic_query.h b/router/src/routing/src/classic_query_sender.h similarity index 60% rename from router/src/routing/src/classic_query.h rename to router/src/routing/src/classic_query_sender.h index ca0c8a0a2a77..dcb0fd604a41 100644 --- a/router/src/routing/src/classic_query.h +++ b/router/src/routing/src/classic_query_sender.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,88 +22,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_QUERY_INCLUDED -#define ROUTING_CLASSIC_QUERY_INCLUDED +#ifndef ROUTING_CLASSIC_QUERY_SENDER_INCLUDED +#define ROUTING_CLASSIC_QUERY_SENDER_INCLUDED #include -#include "forwarding_processor.h" -#include "mysql/harness/stdx/flags.h" - -enum class StmtClassifier { - StateChangeOnSuccess = 1 << 0, // even if tracker doesn't say so. - StateChangeOnError = 1 << 1, // on error - StateChangeOnTracker = 1 << 2, // trust the tracker. - NoStateChangeIgnoreTracker = 1 << 3, // tracker is wrong. - ForbiddenFunctionWithConnSharing = 1 << 4, // forbidden function - ForbiddenSetWithConnSharing = 1 << 5, // forbidden set-tracker -}; - -namespace stdx { -template <> -struct is_flags : std::true_type {}; -} // namespace stdx - -class QueryForwarder : public ForwardingProcessor { - public: - using ForwardingProcessor::ForwardingProcessor; - - enum class Stage { - Command, - - Connect, - Connected, - - Response, - ColumnCount, - Column, - ColumnEnd, - RowOrEnd, - Row, - RowEnd, - - LoadData, - Data, - - Ok, - Error, - - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - [[nodiscard]] Stage stage() const { return stage_; } - - private: - stdx::expected command(); - stdx::expected connect(); - stdx::expected connected(); - stdx::expected response(); - stdx::expected load_data(); - stdx::expected data(); - - stdx::expected column_count(); - stdx::expected column(); - stdx::expected column_end(); - stdx::expected row_or_end(); - stdx::expected row(); - stdx::expected row_end(); - - stdx::expected ok(); - stdx::expected error(); - - stdx::expected track_session_changes( - net::const_buffer session_trackers, - classic_protocol::capabilities::value_type caps); - - stdx::flags stmt_classified_{}; - - Stage stage_{Stage::Command}; - - uint64_t columns_left_{0}; -}; +#include "processor.h" +#include "stmt_classifier.h" class QuerySender : public Processor { public: diff --git a/router/src/routing/src/classic_quit.cc b/router/src/routing/src/classic_quit_forwarder.cc similarity index 87% rename from router/src/routing/src/classic_quit.cc rename to router/src/routing/src/classic_quit_forwarder.cc index 2c33b5019c84..b13a2b61a626 100644 --- a/router/src/routing/src/classic_quit.cc +++ b/router/src/routing/src/classic_quit_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_quit.h" +#include "classic_quit_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" @@ -39,7 +39,7 @@ IMPORT_LOG_FUNCTIONS() /** - * forward the list-fields message flow. + * forward the quit message flow. * * Expected overall flow: * @@ -353,53 +353,3 @@ QuitForwarder::client_tls_shutdown_response() { stage(Stage::Done); return Result::Again; } - -// sender - -stdx::expected QuitSender::process() { - switch (stage()) { - case Stage::Command: - return command(); - case Stage::CloseSocket: - return close_socket(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected QuitSender::command() { - auto *socket_splicer = connection()->socket_splicer(); - auto *dst_protocol = connection()->server_protocol(); - auto *dst_channel = socket_splicer->server_channel(); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("quit::command")); - } - - dst_protocol->seq_id(0xff); - - auto msg_res = - ClassicFrame::send_msg( - dst_channel, dst_protocol, {}); - if (!msg_res) return send_server_failed(msg_res.error()); - - stage(Stage::CloseSocket); - return Result::SendToServer; -} - -stdx::expected QuitSender::close_socket() { - auto *socket_splicer = connection()->socket_splicer(); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event() - .stage("quit::close") - .direction(Tracer::Event::Direction::kServerClose)); - } - - (void)socket_splicer->server_conn().close(); - - stage(Stage::Done); - return Result::Again; -} diff --git a/router/src/routing/src/classic_quit.h b/router/src/routing/src/classic_quit_forwarder.h similarity index 78% rename from router/src/routing/src/classic_quit.h rename to router/src/routing/src/classic_quit_forwarder.h index 02de87e93876..9dc2a46a3831 100644 --- a/router/src/routing/src/classic_quit.h +++ b/router/src/routing/src/classic_quit_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_QUIT_INCLUDED -#define ROUTING_CLASSIC_QUIT_INCLUDED +#ifndef ROUTING_CLASSIC_QUIT_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_QUIT_FORWARDER_INCLUDED #include "forwarding_processor.h" @@ -59,26 +59,4 @@ class QuitForwarder : public ForwardingProcessor { Stage stage_{Stage::Command}; }; -class QuitSender : public Processor { - public: - using Processor::Processor; - - enum class Stage { - Command, - CloseSocket, - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - Stage stage() const { return stage_; } - - private: - stdx::expected command(); - stdx::expected close_socket(); - - Stage stage_{Stage::Command}; -}; - #endif diff --git a/router/src/routing/src/classic_quit_sender.cc b/router/src/routing/src/classic_quit_sender.cc new file mode 100644 index 000000000000..f7210d52453e --- /dev/null +++ b/router/src/routing/src/classic_quit_sender.cc @@ -0,0 +1,88 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_quit_sender.h" + +#include "classic_connection_base.h" +#include "classic_frame.h" +#include "mysql/harness/logging/logging.h" +#include "mysql/harness/net_ts/impl/socket.h" +#include "mysql/harness/net_ts/socket.h" +#include "mysql/harness/stdx/expected.h" +#include "mysql/harness/tls_error.h" +#include "mysqlrouter/connection_pool.h" +#include "mysqlrouter/connection_pool_component.h" +#include "tracer.h" + +IMPORT_LOG_FUNCTIONS() + +// sender + +stdx::expected QuitSender::process() { + switch (stage()) { + case Stage::Command: + return command(); + case Stage::CloseSocket: + return close_socket(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected QuitSender::command() { + auto *socket_splicer = connection()->socket_splicer(); + auto *dst_protocol = connection()->server_protocol(); + auto *dst_channel = socket_splicer->server_channel(); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("quit::command")); + } + + dst_protocol->seq_id(0xff); + + auto msg_res = + ClassicFrame::send_msg( + dst_channel, dst_protocol, {}); + if (!msg_res) return send_server_failed(msg_res.error()); + + stage(Stage::CloseSocket); + return Result::SendToServer; +} + +stdx::expected QuitSender::close_socket() { + auto *socket_splicer = connection()->socket_splicer(); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event() + .stage("quit::close") + .direction(Tracer::Event::Direction::kServerClose)); + } + + (void)socket_splicer->server_conn().close(); + + stage(Stage::Done); + return Result::Again; +} diff --git a/router/src/routing/src/classic_quit_sender.h b/router/src/routing/src/classic_quit_sender.h new file mode 100644 index 000000000000..ac1164f2d63e --- /dev/null +++ b/router/src/routing/src/classic_quit_sender.h @@ -0,0 +1,52 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_QUIT_SENDER_INCLUDED +#define ROUTING_CLASSIC_QUIT_SENDER_INCLUDED + +#include "processor.h" + +class QuitSender : public Processor { + public: + using Processor::Processor; + + enum class Stage { + Command, + CloseSocket, + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + Stage stage() const { return stage_; } + + private: + stdx::expected command(); + stdx::expected close_socket(); + + Stage stage_{Stage::Command}; +}; + +#endif diff --git a/router/src/routing/src/classic_register_replica.cc b/router/src/routing/src/classic_register_replica_forwarder.cc similarity index 97% rename from router/src/routing/src/classic_register_replica.cc rename to router/src/routing/src/classic_register_replica_forwarder.cc index 4ba7e2fc85a0..24dda55f95fd 100644 --- a/router/src/routing/src/classic_register_replica.cc +++ b/router/src/routing/src/classic_register_replica_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,10 +22,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_register_replica.h" +#include "classic_register_replica_forwarder.h" #include "classic_connection_base.h" -#include "classic_forwarder.h" #include "classic_frame.h" #include "classic_lazy_connect.h" #include "mysql/harness/logging/logging.h" diff --git a/router/src/routing/src/classic_register_replica.h b/router/src/routing/src/classic_register_replica_forwarder.h similarity index 91% rename from router/src/routing/src/classic_register_replica.h rename to router/src/routing/src/classic_register_replica_forwarder.h index 40f5a2f33d99..f84d8f1e1b33 100644 --- a/router/src/routing/src/classic_register_replica.h +++ b/router/src/routing/src/classic_register_replica_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_REGISTER_REPLICA_INCLUDED -#define ROUTING_CLASSIC_REGISTER_REPLICA_INCLUDED +#ifndef ROUTING_CLASSIC_REGISTER_REPLICA_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_REGISTER_REPLICA_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_reload.cc b/router/src/routing/src/classic_reload_forwarder.cc similarity index 98% rename from router/src/routing/src/classic_reload.cc rename to router/src/routing/src/classic_reload_forwarder.cc index 55e85d96e310..774ed4fe5cbb 100644 --- a/router/src/routing/src/classic_reload.cc +++ b/router/src/routing/src/classic_reload_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_reload.h" +#include "classic_reload_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_reload.h b/router/src/routing/src/classic_reload_forwarder.h similarity index 90% rename from router/src/routing/src/classic_reload.h rename to router/src/routing/src/classic_reload_forwarder.h index e86d6e032c79..4527c13aa0e1 100644 --- a/router/src/routing/src/classic_reload.h +++ b/router/src/routing/src/classic_reload_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,11 +22,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_RELOAD_INCLUDED -#define ROUTING_CLASSIC_RELOAD_INCLUDED +#ifndef ROUTING_CLASSIC_RELOAD_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_RELOAD_FORWARDER_INCLUDED #include "forwarding_processor.h" -#include "mysqlrouter/classic_protocol_constants.h" class ReloadForwarder : public ForwardingProcessor { public: diff --git a/router/src/routing/src/classic_reset_connection.h b/router/src/routing/src/classic_reset_connection.h deleted file mode 100644 index 5dd8ea91233d..000000000000 --- a/router/src/routing/src/classic_reset_connection.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2.0, - as published by the Free Software Foundation. - - This program is also distributed with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, - as designated in a particular file or component or in included license - documentation. The authors of MySQL hereby grant you an additional - permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef ROUTING_CLASSIC_RESET_CONNECTION_INCLUDED -#define ROUTING_CLASSIC_RESET_CONNECTION_INCLUDED - -#include "forwarding_processor.h" - -class ResetConnectionForwarder : public ForwardingProcessor { - public: - using ForwardingProcessor::ForwardingProcessor; - - enum class Stage { - Command, - Connect, - Connected, - Response, - Ok, - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - Stage stage() const { return stage_; } - - private: - stdx::expected command(); - stdx::expected connect(); - stdx::expected connected(); - stdx::expected response(); - stdx::expected ok(); - - Stage stage_{Stage::Command}; -}; - -class ResetConnectionSender : public Processor { - public: - using Processor::Processor; - - enum class Stage { - Command, - Response, - Ok, - Done, - }; - - stdx::expected process() override; - - void stage(Stage stage) { stage_ = stage; } - Stage stage() const { return stage_; } - - private: - stdx::expected command(); - stdx::expected response(); - stdx::expected ok(); - - Stage stage_{Stage::Command}; -}; - -#endif diff --git a/router/src/routing/src/classic_reset_connection.cc b/router/src/routing/src/classic_reset_connection_forwarder.cc similarity index 68% rename from router/src/routing/src/classic_reset_connection.cc rename to router/src/routing/src/classic_reset_connection_forwarder.cc index 2fef8f2e0970..ae65e95939e8 100644 --- a/router/src/routing/src/classic_reset_connection.cc +++ b/router/src/routing/src/classic_reset_connection_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,13 +22,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_reset_connection.h" +#include "classic_reset_connection_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" #include "classic_frame.h" #include "classic_lazy_connect.h" -#include "classic_query.h" +#include "classic_query_sender.h" #include "mysql/harness/logging/logging.h" #include "mysql/harness/stdx/expected.h" #include "mysql/harness/tls_error.h" @@ -205,98 +205,3 @@ SET @@SESSION.session_track_schema = 'ON', return forward_server_to_client(); } - -stdx::expected -ResetConnectionSender::process() { - switch (stage()) { - case Stage::Command: - return command(); - case Stage::Response: - return response(); - case Stage::Ok: - return ok(); - case Stage::Done: - return Result::Done; - } - - harness_assert_this_should_not_execute(); -} - -stdx::expected -ResetConnectionSender::command() { - auto *socket_splicer = connection()->socket_splicer(); - auto *dst_channel = socket_splicer->server_channel(); - auto *dst_protocol = connection()->server_protocol(); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("reset_connection::command")); - } - - dst_protocol->seq_id(0xff); // reset seq-id - - const auto send_res = ClassicFrame::send_msg< - classic_protocol::borrowed::message::client::ResetConnection>( - dst_channel, dst_protocol, {}); - if (!send_res) return send_server_failed(send_res.error()); - - stage(Stage::Response); - - return Result::SendToServer; -} - -stdx::expected -ResetConnectionSender::response() { - auto *socket_splicer = connection()->socket_splicer(); - auto *src_channel = socket_splicer->server_channel(); - auto *src_protocol = connection()->server_protocol(); - - auto read_res = - ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); - if (!read_res) return recv_server_failed(read_res.error()); - - const uint8_t msg_type = src_protocol->current_msg_type().value(); - - enum class Msg { - Ok = ClassicFrame::cmd_byte(), - }; - - switch (Msg{msg_type}) { - case Msg::Ok: - stage(Stage::Ok); - return Result::Again; - } - - log_debug("reset_connection::response: unexpected msg-type '%02x'", msg_type); - - return recv_server_failed(make_error_code(std::errc::bad_message)); -} - -stdx::expected ResetConnectionSender::ok() { - auto *socket_splicer = connection()->socket_splicer(); - auto *src_channel = socket_splicer->server_channel(); - auto *src_protocol = connection()->server_protocol(); - - auto msg_res = - ClassicFrame::recv_msg( - src_channel, src_protocol); - if (!msg_res) return recv_server_failed(msg_res.error()); - - if (auto &tr = tracer()) { - tr.trace(Tracer::Event().stage("reset_connection::ok")); - } - - auto msg = *msg_res; - - if (!msg.session_changes().empty()) { - auto track_res = connection()->track_session_changes( - net::buffer(msg.session_changes()), - src_protocol->shared_capabilities()); - } - - discard_current_msg(src_channel, src_protocol); - - connection()->connection_sharing_allowed_reset(); - - stage(Stage::Done); - return Result::Again; -} diff --git a/router/src/routing/src/classic_reset_connection_forwarder.h b/router/src/routing/src/classic_reset_connection_forwarder.h new file mode 100644 index 000000000000..e095dab7dee4 --- /dev/null +++ b/router/src/routing/src/classic_reset_connection_forwarder.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_RESET_CONNECTION_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_RESET_CONNECTION_FORWARDER_INCLUDED + +#include "forwarding_processor.h" + +class ResetConnectionForwarder : public ForwardingProcessor { + public: + using ForwardingProcessor::ForwardingProcessor; + + enum class Stage { + Command, + Connect, + Connected, + Response, + Ok, + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + Stage stage() const { return stage_; } + + private: + stdx::expected command(); + stdx::expected connect(); + stdx::expected connected(); + stdx::expected response(); + stdx::expected ok(); + + Stage stage_{Stage::Command}; +}; + +#endif diff --git a/router/src/routing/src/classic_reset_connection_sender.cc b/router/src/routing/src/classic_reset_connection_sender.cc new file mode 100644 index 000000000000..d027a721f925 --- /dev/null +++ b/router/src/routing/src/classic_reset_connection_sender.cc @@ -0,0 +1,129 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "classic_reset_connection_sender.h" + +#include "classic_connection_base.h" +#include "classic_frame.h" +#include "mysql/harness/logging/logging.h" +#include "mysql/harness/stdx/expected.h" +#include "mysql/harness/tls_error.h" +#include "tracer.h" + +IMPORT_LOG_FUNCTIONS() + +stdx::expected +ResetConnectionSender::process() { + switch (stage()) { + case Stage::Command: + return command(); + case Stage::Response: + return response(); + case Stage::Ok: + return ok(); + case Stage::Done: + return Result::Done; + } + + harness_assert_this_should_not_execute(); +} + +stdx::expected +ResetConnectionSender::command() { + auto *socket_splicer = connection()->socket_splicer(); + auto *dst_channel = socket_splicer->server_channel(); + auto *dst_protocol = connection()->server_protocol(); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("reset_connection::command")); + } + + dst_protocol->seq_id(0xff); // reset seq-id + + const auto send_res = ClassicFrame::send_msg< + classic_protocol::borrowed::message::client::ResetConnection>( + dst_channel, dst_protocol, {}); + if (!send_res) return send_server_failed(send_res.error()); + + stage(Stage::Response); + + return Result::SendToServer; +} + +stdx::expected +ResetConnectionSender::response() { + auto *socket_splicer = connection()->socket_splicer(); + auto *src_channel = socket_splicer->server_channel(); + auto *src_protocol = connection()->server_protocol(); + + auto read_res = + ClassicFrame::ensure_has_msg_prefix(src_channel, src_protocol); + if (!read_res) return recv_server_failed(read_res.error()); + + const uint8_t msg_type = src_protocol->current_msg_type().value(); + + enum class Msg { + Ok = ClassicFrame::cmd_byte(), + }; + + switch (Msg{msg_type}) { + case Msg::Ok: + stage(Stage::Ok); + return Result::Again; + } + + log_debug("reset_connection::response: unexpected msg-type '%02x'", msg_type); + + return recv_server_failed(make_error_code(std::errc::bad_message)); +} + +stdx::expected ResetConnectionSender::ok() { + auto *socket_splicer = connection()->socket_splicer(); + auto *src_channel = socket_splicer->server_channel(); + auto *src_protocol = connection()->server_protocol(); + + auto msg_res = + ClassicFrame::recv_msg( + src_channel, src_protocol); + if (!msg_res) return recv_server_failed(msg_res.error()); + + if (auto &tr = tracer()) { + tr.trace(Tracer::Event().stage("reset_connection::ok")); + } + + auto msg = *msg_res; + + if (!msg.session_changes().empty()) { + auto track_res = connection()->track_session_changes( + net::buffer(msg.session_changes()), + src_protocol->shared_capabilities()); + } + + discard_current_msg(src_channel, src_protocol); + + connection()->connection_sharing_allowed_reset(); + + stage(Stage::Done); + return Result::Again; +} diff --git a/router/src/routing/src/classic_reset_connection_sender.h b/router/src/routing/src/classic_reset_connection_sender.h new file mode 100644 index 000000000000..692950f2f3e2 --- /dev/null +++ b/router/src/routing/src/classic_reset_connection_sender.h @@ -0,0 +1,54 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_CLASSIC_RESET_CONNECTION_SENDER_INCLUDED +#define ROUTING_CLASSIC_RESET_CONNECTION_SENDER_INCLUDED + +#include "processor.h" + +class ResetConnectionSender : public Processor { + public: + using Processor::Processor; + + enum class Stage { + Command, + Response, + Ok, + Done, + }; + + stdx::expected process() override; + + void stage(Stage stage) { stage_ = stage; } + Stage stage() const { return stage_; } + + private: + stdx::expected command(); + stdx::expected response(); + stdx::expected ok(); + + Stage stage_{Stage::Command}; +}; + +#endif diff --git a/router/src/routing/src/classic_set_option.cc b/router/src/routing/src/classic_set_option_forwarder.cc similarity index 98% rename from router/src/routing/src/classic_set_option.cc rename to router/src/routing/src/classic_set_option_forwarder.cc index b986973ae731..b62031b1a4a7 100644 --- a/router/src/routing/src/classic_set_option.cc +++ b/router/src/routing/src/classic_set_option_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_set_option.h" +#include "classic_set_option_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_set_option.h b/router/src/routing/src/classic_set_option_forwarder.h similarity index 91% rename from router/src/routing/src/classic_set_option.h rename to router/src/routing/src/classic_set_option_forwarder.h index 2d41110f64dd..1d296f3fcdbd 100644 --- a/router/src/routing/src/classic_set_option.h +++ b/router/src/routing/src/classic_set_option_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_STMT_SET_OPTION_INCLUDED -#define ROUTING_CLASSIC_STMT_SET_OPTION_INCLUDED +#ifndef ROUTING_CLASSIC_STMT_SET_OPTION_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_STMT_SET_OPTION_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_statistics.cc b/router/src/routing/src/classic_statistics_forwarder.cc similarity index 97% rename from router/src/routing/src/classic_statistics.cc rename to router/src/routing/src/classic_statistics_forwarder.cc index 168ad304ea77..ee2986740e61 100644 --- a/router/src/routing/src/classic_statistics.cc +++ b/router/src/routing/src/classic_statistics_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_statistics.h" +#include "classic_statistics_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_statistics.h b/router/src/routing/src/classic_statistics_forwarder.h similarity index 91% rename from router/src/routing/src/classic_statistics.h rename to router/src/routing/src/classic_statistics_forwarder.h index 4e794f6cf015..e7f3d09e2f5f 100644 --- a/router/src/routing/src/classic_statistics.h +++ b/router/src/routing/src/classic_statistics_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_STATISTICS_INCLUDED -#define ROUTING_CLASSIC_STATISTICS_INCLUDED +#ifndef ROUTING_CLASSIC_STATISTICS_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_STATISTICS_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_stmt_close.cc b/router/src/routing/src/classic_stmt_close_forwarder.cc similarity index 96% rename from router/src/routing/src/classic_stmt_close.cc rename to router/src/routing/src/classic_stmt_close_forwarder.cc index 9b64602a5b10..66969d86e22a 100644 --- a/router/src/routing/src/classic_stmt_close.cc +++ b/router/src/routing/src/classic_stmt_close_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_stmt_close.h" +#include "classic_stmt_close_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_stmt_close.h b/router/src/routing/src/classic_stmt_close_forwarder.h similarity index 90% rename from router/src/routing/src/classic_stmt_close.h rename to router/src/routing/src/classic_stmt_close_forwarder.h index 397cc2cac4c8..0fa7d7c94bc5 100644 --- a/router/src/routing/src/classic_stmt_close.h +++ b/router/src/routing/src/classic_stmt_close_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_STMT_CLOSE_INCLUDED -#define ROUTING_CLASSIC_STMT_CLOSE_INCLUDED +#ifndef ROUTING_CLASSIC_STMT_CLOSE_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_STMT_CLOSE_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_stmt_execute.cc b/router/src/routing/src/classic_stmt_execute_forwarder.cc similarity index 98% rename from router/src/routing/src/classic_stmt_execute.cc rename to router/src/routing/src/classic_stmt_execute_forwarder.cc index e134a6d79d03..601e67dc7b2c 100644 --- a/router/src/routing/src/classic_stmt_execute.cc +++ b/router/src/routing/src/classic_stmt_execute_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_stmt_execute.h" +#include "classic_stmt_execute_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_stmt_execute.h b/router/src/routing/src/classic_stmt_execute_forwarder.h similarity index 92% rename from router/src/routing/src/classic_stmt_execute.h rename to router/src/routing/src/classic_stmt_execute_forwarder.h index 689bb834d4c7..31713b38b4e6 100644 --- a/router/src/routing/src/classic_stmt_execute.h +++ b/router/src/routing/src/classic_stmt_execute_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_STMT_EXECUTE_INCLUDED -#define ROUTING_CLASSIC_STMT_EXECUTE_INCLUDED +#ifndef ROUTING_CLASSIC_STMT_EXECUTE_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_STMT_EXECUTE_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_stmt_fetch.cc b/router/src/routing/src/classic_stmt_fetch_forwarder.cc similarity index 98% rename from router/src/routing/src/classic_stmt_fetch.cc rename to router/src/routing/src/classic_stmt_fetch_forwarder.cc index e312edb889c9..5492704a089b 100644 --- a/router/src/routing/src/classic_stmt_fetch.cc +++ b/router/src/routing/src/classic_stmt_fetch_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_stmt_fetch.h" +#include "classic_stmt_fetch_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_stmt_fetch.h b/router/src/routing/src/classic_stmt_fetch_forwarder.h similarity index 91% rename from router/src/routing/src/classic_stmt_fetch.h rename to router/src/routing/src/classic_stmt_fetch_forwarder.h index c4fc4cfaba22..d1c345707742 100644 --- a/router/src/routing/src/classic_stmt_fetch.h +++ b/router/src/routing/src/classic_stmt_fetch_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_STMT_FETCH_INCLUDED -#define ROUTING_CLASSIC_STMT_FETCH_INCLUDED +#ifndef ROUTING_CLASSIC_STMT_FETCH_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_STMT_FETCH_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_stmt_param_append_data.cc b/router/src/routing/src/classic_stmt_param_append_data_forwarder.cc similarity index 96% rename from router/src/routing/src/classic_stmt_param_append_data.cc rename to router/src/routing/src/classic_stmt_param_append_data_forwarder.cc index 06c34ddf549e..fc5808b0846b 100644 --- a/router/src/routing/src/classic_stmt_param_append_data.cc +++ b/router/src/routing/src/classic_stmt_param_append_data_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_stmt_param_append_data.h" +#include "classic_stmt_param_append_data_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_stmt_param_append_data.h b/router/src/routing/src/classic_stmt_param_append_data_forwarder.h similarity index 89% rename from router/src/routing/src/classic_stmt_param_append_data.h rename to router/src/routing/src/classic_stmt_param_append_data_forwarder.h index 3f713e37110f..5bf318c243a6 100644 --- a/router/src/routing/src/classic_stmt_param_append_data.h +++ b/router/src/routing/src/classic_stmt_param_append_data_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_STMT_PARAM_APPEND_DATA_INCLUDED -#define ROUTING_CLASSIC_STMT_PARAM_APPEND_DATA_INCLUDED +#ifndef ROUTING_CLASSIC_STMT_PARAM_APPEND_DATA_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_STMT_PARAM_APPEND_DATA_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_stmt_prepare.cc b/router/src/routing/src/classic_stmt_prepare_forwarder.cc similarity index 98% rename from router/src/routing/src/classic_stmt_prepare.cc rename to router/src/routing/src/classic_stmt_prepare_forwarder.cc index 48602ec6fdb6..71522790b2b6 100644 --- a/router/src/routing/src/classic_stmt_prepare.cc +++ b/router/src/routing/src/classic_stmt_prepare_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_stmt_prepare.h" +#include "classic_stmt_prepare_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_stmt_prepare.h b/router/src/routing/src/classic_stmt_prepare_forwarder.h similarity index 93% rename from router/src/routing/src/classic_stmt_prepare.h rename to router/src/routing/src/classic_stmt_prepare_forwarder.h index a7b734144c8d..34caab395fa3 100644 --- a/router/src/routing/src/classic_stmt_prepare.h +++ b/router/src/routing/src/classic_stmt_prepare_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_STMT_PREPARE_INCLUDED -#define ROUTING_CLASSIC_STMT_PREPARE_INCLUDED +#ifndef ROUTING_CLASSIC_STMT_PREPARE_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_STMT_PREPARE_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/classic_stmt_reset.cc b/router/src/routing/src/classic_stmt_reset_forwarder.cc similarity index 97% rename from router/src/routing/src/classic_stmt_reset.cc rename to router/src/routing/src/classic_stmt_reset_forwarder.cc index 6de831841fc1..baeab0407e68 100644 --- a/router/src/routing/src/classic_stmt_reset.cc +++ b/router/src/routing/src/classic_stmt_reset_forwarder.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_stmt_reset.h" +#include "classic_stmt_reset_forwarder.h" #include "classic_connection_base.h" #include "classic_forwarder.h" diff --git a/router/src/routing/src/classic_stmt_reset.h b/router/src/routing/src/classic_stmt_reset_forwarder.h similarity index 91% rename from router/src/routing/src/classic_stmt_reset.h rename to router/src/routing/src/classic_stmt_reset_forwarder.h index adbb61da17f9..0d6fdba7da98 100644 --- a/router/src/routing/src/classic_stmt_reset.h +++ b/router/src/routing/src/classic_stmt_reset_forwarder.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2022, 2023, Oracle and/or its affiliates. + Copyright (c) 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -22,8 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ROUTING_CLASSIC_STMT_RESET_INCLUDED -#define ROUTING_CLASSIC_STMT_RESET_INCLUDED +#ifndef ROUTING_CLASSIC_STMT_RESET_FORWARDER_INCLUDED +#define ROUTING_CLASSIC_STMT_RESET_FORWARDER_INCLUDED #include "forwarding_processor.h" diff --git a/router/src/routing/src/stmt_classifier.h b/router/src/routing/src/stmt_classifier.h new file mode 100644 index 000000000000..61357bb0a6c0 --- /dev/null +++ b/router/src/routing/src/stmt_classifier.h @@ -0,0 +1,44 @@ +/* + Copyright (c) 2022, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ROUTING_STMT_CLASSIFIER_INCLUDED +#define ROUTING_STMT_CLASSIFIER_INCLUDED + +#include "mysql/harness/stdx/flags.h" + +enum class StmtClassifier { + StateChangeOnSuccess = 1 << 0, // even if tracker doesn't say so. + StateChangeOnError = 1 << 1, // on error + StateChangeOnTracker = 1 << 2, // trust the tracker. + NoStateChangeIgnoreTracker = 1 << 3, // tracker is wrong. + ForbiddenFunctionWithConnSharing = 1 << 4, // forbidden function + ForbiddenSetWithConnSharing = 1 << 5, // forbidden set-tracker +}; + +namespace stdx { +template <> +struct is_flags : std::true_type {}; +} // namespace stdx + +#endif diff --git a/router/src/routing/tests/test_classic_quit_sender.cc b/router/src/routing/tests/test_classic_quit_sender.cc index ac4c539b086e..34b4533c938c 100644 --- a/router/src/routing/tests/test_classic_quit_sender.cc +++ b/router/src/routing/tests/test_classic_quit_sender.cc @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_quit.h" +#include "classic_quit_sender.h" #include diff --git a/router/src/routing/tests/test_classic_reset_connection_sender.cc b/router/src/routing/tests/test_classic_reset_connection_sender.cc index 557baac80829..71d1829ad0fa 100644 --- a/router/src/routing/tests/test_classic_reset_connection_sender.cc +++ b/router/src/routing/tests/test_classic_reset_connection_sender.cc @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "classic_reset_connection.h" +#include "classic_reset_connection_sender.h" #include "classic_connection_base.h" #include "hexify.h"