chore(mysqlx): retire MysqlxFrontendSession, MysqlxBackendSession, X_FAST_FORWARD

Three pieces of dead code that survived the MysqlxWorker retirement
(commit 98aee7db2) and the v2 event-driven rewrite:

* `MysqlxFrontendSession` and `MysqlxBackendSession` (plugins/mysqlx/
  include/* + src/*): an alternative session implementation from the
  pre-event-driven days.  Compiled into the .so but not instantiated
  anywhere — `Mysqlx_Thread::accept_new_connection` creates a
  `MysqlxSession` directly.  The Phase-1 placeholder in
  `MysqlxFrontendSession::handler_capabilities_set` (returning
  "TLS capability not supported by mysqlx plugin in Phase 1") was
  particularly misleading on review since the real, TLS-aware
  `MysqlxSession::handler_capabilities_set` in mysqlx_session.cpp:202
  has full TLS negotiation wired up.

* `X_FAST_FORWARD` enum value, `handler_fast_forward()` declaration,
  dispatch case, and empty body (plugins/mysqlx/src/mysqlx_session.cpp):
  state was declared but no code path ever set
  `status_ = X_FAST_FORWARD`, so the dispatch case was unreachable and
  the handler body was empty.

Drop the four files from `plugins/mysqlx/Makefile`'s SRCS list and
delete them.  Plugin .so size drops from ~9.3 MB to ~8.5 MB
(approximately 800 KB of dead code eliminated).  Build clean under
PROXYSQLGENAI=1.
ProtocolX
Rene Cannao 1 month ago
parent c723ede0cf
commit 79cac4c976

@ -105,8 +105,6 @@ SRCS := $(PLUGIN_DIR)/src/mysqlx_plugin.cpp \
$(PLUGIN_DIR)/src/mysqlx_config_store.cpp \
$(PLUGIN_DIR)/src/mysqlx_listener_reconcile.cpp \
$(PLUGIN_DIR)/src/mysqlx_protocol.cpp \
$(PLUGIN_DIR)/src/mysqlx_frontend_session.cpp \
$(PLUGIN_DIR)/src/mysqlx_backend_session.cpp \
$(PLUGIN_DIR)/src/mysqlx_data_stream.cpp \
$(PLUGIN_DIR)/src/mysqlx_connection.cpp \
$(PLUGIN_DIR)/src/mysqlx_stats.cpp \

@ -1,35 +0,0 @@
#ifndef PROXYSQL_MYSQLX_BACKEND_SESSION_H
#define PROXYSQL_MYSQLX_BACKEND_SESSION_H
#include "mysqlx_config_store.h"
#include <string>
class MysqlxBackendSession {
public:
MysqlxBackendSession();
~MysqlxBackendSession();
MysqlxBackendSession(const MysqlxBackendSession&) = delete;
MysqlxBackendSession& operator=(const MysqlxBackendSession&) = delete;
// Connect to backend and authenticate using resolved identity and endpoint.
bool connect(const MysqlxResolvedIdentity& identity,
const MysqlxBackendEndpoint& endpoint,
std::string& err);
int fd() const { return backend_fd_; }
// Relay data bidirectionally between frontend_fd and backend_fd.
// Runs until one side closes or an error occurs.
bool relay(int frontend_fd);
private:
bool authenticate_backend(const std::string& username,
const std::string& password,
std::string& err);
int backend_fd_ { -1 };
};
#endif /* PROXYSQL_MYSQLX_BACKEND_SESSION_H */

@ -1,43 +0,0 @@
#ifndef PROXYSQL_MYSQLX_FRONTEND_SESSION_H
#define PROXYSQL_MYSQLX_FRONTEND_SESSION_H
#include "mysqlx_config_store.h"
#include "mysqlx_protocol.h"
#include <cstdint>
#include <string>
#include <vector>
class MysqlxFrontendSession {
public:
explicit MysqlxFrontendSession(int client_fd);
~MysqlxFrontendSession();
MysqlxFrontendSession(const MysqlxFrontendSession&) = delete;
MysqlxFrontendSession& operator=(const MysqlxFrontendSession&) = delete;
// Run the full X Protocol handshake and authentication.
// On success, the session is authenticated and identity is resolved.
// On failure, an error has already been sent to the client.
bool run_handshake_and_auth(MysqlxConfigStore& config_store);
// After successful auth, returns the resolved identity.
const MysqlxResolvedIdentity& identity() const { return identity_; }
int client_fd() const { return client_fd_; }
private:
bool handle_capabilities_get();
bool handle_capabilities_set(const std::vector<uint8_t>& payload);
bool handle_authenticate(MysqlxConfigStore& config_store);
bool send_capabilities();
bool send_auth_continue_challenge();
int client_fd_;
MysqlxResolvedIdentity identity_ {};
std::vector<uint8_t> auth_challenge_ {};
std::string auth_method_ {};
};
#endif /* PROXYSQL_MYSQLX_FRONTEND_SESSION_H */

@ -47,7 +47,6 @@ public:
PROCESSING_X_QUERY,
CONNECTING_SERVER,
WAITING_SERVER_XMSG,
X_FAST_FORWARD,
X_TLS_ACCEPT_INIT,
X_TLS_ACCEPT_CONT,
X_TLS_ACCEPT_DONE,
@ -117,7 +116,6 @@ private:
void handler_auth_challenge_response();
void handler_waiting_client_msg();
void handler_waiting_server_msg();
void handler_fast_forward();
void handler_session_closing();
void handler_connecting_server();
void handler_session_reset_waiting();

@ -1,309 +0,0 @@
#include "mysqlx_backend_session.h"
#include "mysqlx_protocol.h"
#include "mysqlx.pb.h"
#include "mysqlx_connection.pb.h"
#include "mysqlx_session.pb.h"
#include <arpa/inet.h>
#include <cerrno>
#include <cstring>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <sys/socket.h>
#include <unistd.h>
MysqlxBackendSession::MysqlxBackendSession() = default;
MysqlxBackendSession::~MysqlxBackendSession() {
if (backend_fd_ >= 0) {
close(backend_fd_);
backend_fd_ = -1;
}
}
bool MysqlxBackendSession::connect(const MysqlxResolvedIdentity& identity,
const MysqlxBackendEndpoint& endpoint,
std::string& err) {
if (identity.backend_auth_mode == MysqlxBackendAuthMode::pass_through) {
err = "pass_through backend auth mode not supported in Phase 1";
return false;
}
if (endpoint.hostname.empty() || endpoint.mysqlx_port <= 0) {
err = "no valid backend endpoint";
return false;
}
std::string port_str = std::to_string(endpoint.mysqlx_port);
struct addrinfo hints {}, *result = nullptr;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int gai_rc = getaddrinfo(endpoint.hostname.c_str(), port_str.c_str(), &hints, &result);
if (gai_rc != 0) {
err = "mysqlx backend: cannot resolve '";
err += endpoint.hostname;
err += "': ";
err += gai_strerror(gai_rc);
return false;
}
int fd = -1;
for (struct addrinfo* rp = result; rp != nullptr; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd < 0) continue;
int flag = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
struct timeval tv { 10, 0 };
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
if (::connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) {
break;
}
close(fd);
fd = -1;
}
freeaddrinfo(result);
if (fd < 0) {
err = "mysqlx backend: connect failed to ";
err += endpoint.hostname + ":" + std::to_string(endpoint.mysqlx_port);
return false;
}
backend_fd_ = fd;
// Authenticate to the backend MySQL X server.
std::string backend_user = identity.backend_username;
std::string backend_pass = identity.backend_password;
if (!authenticate_backend(backend_user, backend_pass, err)) {
// Auth failure: close the socket we just opened so a later retry
// on this MysqlxBackendSession doesn't overwrite backend_fd_ and
// leak the previous fd.
close(backend_fd_);
backend_fd_ = -1;
return false;
}
return true;
}
bool MysqlxBackendSession::authenticate_backend(const std::string& username,
const std::string& password,
std::string& err) {
// Step 1: Send CapabilitiesGet to learn what the backend supports.
{
Mysqlx::Connection::CapabilitiesGet cap_get;
std::string serialized;
cap_get.SerializeToString(&serialized);
auto frame = mysqlx_build_frame(
Mysqlx::ClientMessages_Type_CON_CAPABILITIES_GET,
serialized
);
if (!mysqlx_write_all(backend_fd_, frame.data(), frame.size())) {
err = "failed to send CapabilitiesGet to backend";
return false;
}
}
// Step 2: Read Capabilities response (skip any preceding Notice frames).
{
MysqlxFrameHeader header {};
std::vector<uint8_t> payload {};
if (!mysqlx_read_frame(backend_fd_, header, payload)) {
err = "failed to read Capabilities from backend";
return false;
}
while (header.message_type == Mysqlx::ServerMessages_Type_NOTICE) {
if (!mysqlx_read_frame(backend_fd_, header, payload)) {
err = "failed to read frame after notice during capabilities";
return false;
}
}
if (header.message_type != Mysqlx::ServerMessages_Type_CONN_CAPABILITIES) {
err = "unexpected response to CapabilitiesGet from backend";
return false;
}
}
// Step 3: Send AuthenticateStart with MYSQL41.
{
Mysqlx::Session::AuthenticateStart auth_start;
auth_start.set_mech_name("MYSQL41");
// auth_data: schema\0username\0
std::string auth_data;
auth_data += '\0'; // empty schema
auth_data += username;
auth_data += '\0';
auth_start.set_auth_data(auth_data);
std::string serialized;
auth_start.SerializeToString(&serialized);
auto frame = mysqlx_build_frame(
Mysqlx::ClientMessages_Type_SESS_AUTHENTICATE_START,
serialized
);
if (!mysqlx_write_all(backend_fd_, frame.data(), frame.size())) {
err = "failed to send AuthenticateStart to backend";
return false;
}
}
// Step 4: Read AuthenticateContinue with challenge (skip Notice frames).
std::vector<uint8_t> challenge {};
{
MysqlxFrameHeader header {};
std::vector<uint8_t> payload {};
if (!mysqlx_read_frame(backend_fd_, header, payload)) {
err = "failed to read auth challenge from backend";
return false;
}
// Skip any intervening Notice frames.
while (header.message_type == Mysqlx::ServerMessages_Type_NOTICE) {
if (!mysqlx_read_frame(backend_fd_, header, payload)) {
err = "failed to read frame after notice during auth challenge";
return false;
}
}
if (header.message_type == Mysqlx::ServerMessages_Type_ERROR) {
Mysqlx::Error error_msg;
error_msg.ParseFromArray(payload.data(), static_cast<int>(payload.size()));
err = "backend auth error: " + error_msg.msg();
return false;
}
if (header.message_type != Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_CONTINUE) {
err = "unexpected message type during backend auth";
return false;
}
Mysqlx::Session::AuthenticateContinue auth_continue;
if (!auth_continue.ParseFromArray(payload.data(), static_cast<int>(payload.size()))) {
err = "failed to parse backend AuthenticateContinue";
return false;
}
const std::string& ch = auth_continue.auth_data();
challenge.assign(ch.begin(), ch.end());
}
// Step 5: Compute MYSQL41 scramble and send AuthenticateContinue.
// Wire format: \0username\0*UPPERCASE_HEX(scramble)
{
auto scramble = mysqlx_mysql41_scramble(challenge, password);
std::string hex_scramble = mysqlx_hex_encode(scramble);
std::string auth_data;
auth_data += '\0'; // empty schema
auth_data += username;
auth_data += '\0';
auth_data += '*';
auth_data += hex_scramble;
Mysqlx::Session::AuthenticateContinue auth_continue;
auth_continue.set_auth_data(auth_data);
std::string serialized;
auth_continue.SerializeToString(&serialized);
auto frame = mysqlx_build_frame(
Mysqlx::ClientMessages_Type_SESS_AUTHENTICATE_CONTINUE,
serialized
);
if (!mysqlx_write_all(backend_fd_, frame.data(), frame.size())) {
err = "failed to send auth response to backend";
return false;
}
}
// Step 6: Read AuthenticateOk (or Error).
{
MysqlxFrameHeader header {};
std::vector<uint8_t> payload {};
if (!mysqlx_read_frame(backend_fd_, header, payload)) {
err = "failed to read auth result from backend";
return false;
}
if (header.message_type == Mysqlx::ServerMessages_Type_ERROR) {
Mysqlx::Error error_msg;
error_msg.ParseFromArray(payload.data(), static_cast<int>(payload.size()));
err = "backend auth failed: " + error_msg.msg();
return false;
}
// Accept both AuthenticateOk and Notice (some servers send notices first).
while (header.message_type == Mysqlx::ServerMessages_Type_NOTICE) {
if (!mysqlx_read_frame(backend_fd_, header, payload)) {
err = "failed to read post-notice frame from backend";
return false;
}
}
if (header.message_type != Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_OK) {
err = "unexpected message type after backend auth";
return false;
}
}
return true;
}
bool MysqlxBackendSession::relay(int frontend_fd) {
// Byte-level bidirectional relay between frontend and backend.
uint8_t buf[65536];
struct pollfd pfds[2];
pfds[0].fd = frontend_fd;
pfds[0].events = POLLIN;
pfds[1].fd = backend_fd_;
pfds[1].events = POLLIN;
while (true) {
pfds[0].revents = 0;
pfds[1].revents = 0;
int ready = poll(pfds, 2, 30000 /*ms*/);
if (ready < 0) {
return false;
}
if (ready == 0) {
return false; // idle timeout
}
// Check for errors/hangups.
if ((pfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) ||
(pfds[1].revents & (POLLERR | POLLHUP | POLLNVAL))) {
return false;
}
// Frontend → Backend
if (pfds[0].revents & POLLIN) {
ssize_t n = read(frontend_fd, buf, sizeof(buf));
if (n <= 0) {
return n == 0;
}
if (!mysqlx_write_all(backend_fd_, buf, static_cast<size_t>(n))) {
return false;
}
}
// Backend → Frontend
if (pfds[1].revents & POLLIN) {
ssize_t n = read(backend_fd_, buf, sizeof(buf));
if (n <= 0) {
return n == 0;
}
if (!mysqlx_write_all(frontend_fd, buf, static_cast<size_t>(n))) {
return false;
}
}
}
}

@ -1,406 +0,0 @@
#include "mysqlx_frontend_session.h"
#include "mysqlx.pb.h"
#include "mysqlx_connection.pb.h"
#include "mysqlx_session.pb.h"
#include "mysqlx_notice.pb.h"
#include <cstdlib>
#include <cstring>
#include <openssl/crypto.h>
#include <openssl/rand.h>
#include <sys/socket.h>
#include <unistd.h>
namespace {
constexpr size_t CHALLENGE_LENGTH = 20;
// Parse "schema\0user\0" from auth_data (MYSQL41 initial message).
// Returns false if parsing fails.
bool parse_mysql41_auth_data(const std::string& auth_data,
std::string& schema,
std::string& username) {
// Format: \0-terminated schema, then \0-terminated username, then scramble.
// For AuthenticateStart, auth_data is just schema + \0 + user + \0.
size_t first_nul = auth_data.find('\0');
if (first_nul == std::string::npos) {
return false;
}
schema = auth_data.substr(0, first_nul);
size_t rest_start = first_nul + 1;
size_t second_nul = auth_data.find('\0', rest_start);
if (second_nul == std::string::npos) {
// No second NUL — everything after first NUL is username.
username = auth_data.substr(rest_start);
} else {
username = auth_data.substr(rest_start, second_nul - rest_start);
}
return !username.empty();
}
// Parse PLAIN auth data: \0username\0password
bool parse_plain_auth_data(const std::string& auth_data,
std::string& username,
std::string& password) {
// Format: \0user\0password
if (auth_data.empty() || auth_data[0] != '\0') {
return false;
}
size_t second_nul = auth_data.find('\0', 1);
if (second_nul == std::string::npos) {
return false;
}
username = auth_data.substr(1, second_nul - 1);
password = auth_data.substr(second_nul + 1);
return !username.empty();
}
// Check if the auth method is allowed for this user.
// Empty allowed_auth_methods means all supported methods are allowed.
bool is_auth_method_allowed(const std::string& method, const std::string& allowed) {
if (allowed.empty()) {
return true;
}
// allowed_auth_methods is comma-separated, e.g. "MYSQL41,PLAIN"
size_t pos = 0;
while (pos < allowed.size()) {
size_t comma = allowed.find(',', pos);
if (comma == std::string::npos) comma = allowed.size();
std::string token = allowed.substr(pos, comma - pos);
// Trim whitespace.
while (!token.empty() && token.front() == ' ') token.erase(0, 1);
while (!token.empty() && token.back() == ' ') token.pop_back();
if (token == method) return true;
pos = comma + 1;
}
return false;
}
} // namespace
MysqlxFrontendSession::MysqlxFrontendSession(int client_fd)
: client_fd_(client_fd) {
// Set socket timeouts to prevent slow-client DoS.
struct timeval tv { 30, 0 }; // 30 second timeout
setsockopt(client_fd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(client_fd_, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
// Generate random challenge for MYSQL41.
auth_challenge_.resize(CHALLENGE_LENGTH);
RAND_bytes(auth_challenge_.data(), CHALLENGE_LENGTH);
}
MysqlxFrontendSession::~MysqlxFrontendSession() = default;
bool MysqlxFrontendSession::run_handshake_and_auth(MysqlxConfigStore& config_store) {
// X Protocol handshake loop:
// 1. Wait for CapabilitiesGet or AuthenticateStart.
// 2. If CapabilitiesGet → reply with Capabilities, then wait for next.
// 3. If CapabilitiesSet → handle TLS etc, reply Ok, wait for next.
// 4. If AuthenticateStart → run auth flow.
while (true) {
MysqlxFrameHeader header {};
std::vector<uint8_t> payload {};
if (!mysqlx_read_frame(client_fd_, header, payload)) {
return false;
}
switch (header.message_type) {
case Mysqlx::ClientMessages_Type_CON_CAPABILITIES_GET:
if (!handle_capabilities_get()) {
return false;
}
break;
case Mysqlx::ClientMessages_Type_CON_CAPABILITIES_SET:
if (!handle_capabilities_set(payload)) {
return false;
}
break;
case Mysqlx::ClientMessages_Type_SESS_AUTHENTICATE_START: {
// Parse AuthenticateStart.
Mysqlx::Session::AuthenticateStart auth_start;
if (!auth_start.ParseFromArray(payload.data(), static_cast<int>(payload.size()))) {
mysqlx_send_error(client_fd_, 1045, "Invalid AuthenticateStart message");
return false;
}
auth_method_ = auth_start.mech_name();
if (!mysqlx_is_supported_auth_method(auth_method_)) {
mysqlx_send_error(client_fd_, 1251,
"Authentication method '" + auth_method_ + "' not supported");
return false;
}
if (auth_method_ == "PLAIN") {
// PLAIN: auth_data contains \0user\0password in one shot.
std::string username {};
std::string password {};
if (!parse_plain_auth_data(auth_start.auth_data(), username, password)) {
mysqlx_send_error(client_fd_, 1045, "Invalid PLAIN auth data");
return false;
}
auto resolved = config_store.resolve_identity(username);
if (!resolved.has_value()) {
mysqlx_send_error(client_fd_, 1045,
"Access denied for user '" + username + "'");
return false;
}
identity_ = resolved.value();
if (!identity_.x_enabled) {
mysqlx_send_error(client_fd_, 1045,
"X Protocol access not enabled for user '" + username + "'");
return false;
}
if (identity_.require_tls) {
mysqlx_send_error(client_fd_, 1045,
"User '" + username + "' requires TLS for X Protocol access");
return false;
}
if (!is_auth_method_allowed("PLAIN", identity_.allowed_auth_methods)) {
mysqlx_send_error(client_fd_, 1251,
"Authentication method 'PLAIN' not allowed for user '" + username + "'");
return false;
}
if (identity_.backend_auth_mode == MysqlxBackendAuthMode::pass_through) {
mysqlx_send_error(client_fd_, 1045,
"pass_through backend auth mode not supported in Phase 1");
return false;
}
// For PLAIN, verify password against backend_password using
// constant-time comparison to prevent timing side-channels.
bool pwd_match = false;
if (!password.empty() && !identity_.backend_password.empty() &&
password.size() == identity_.backend_password.size()) {
pwd_match = CRYPTO_memcmp(password.data(), identity_.backend_password.data(),
password.size()) == 0;
}
if (!pwd_match) {
mysqlx_send_error(client_fd_, 1045,
"Access denied for user '" + username + "'");
return false;
}
// Send AuthenticateOk.
Mysqlx::Session::AuthenticateOk auth_ok;
std::string serialized;
auth_ok.SerializeToString(&serialized);
auto frame = mysqlx_build_frame(
Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_OK,
serialized
);
if (!mysqlx_write_all(client_fd_, frame.data(), frame.size())) {
return false;
}
return true;
}
if (auth_method_ == "MYSQL41") {
// MYSQL41 phase 1: parse schema+user, send challenge.
std::string schema {};
std::string username {};
if (!parse_mysql41_auth_data(auth_start.auth_data(), schema, username)) {
mysqlx_send_error(client_fd_, 1045, "Invalid MYSQL41 auth data");
return false;
}
auto resolved = config_store.resolve_identity(username);
if (!resolved.has_value()) {
mysqlx_send_error(client_fd_, 1045,
"Access denied for user '" + username + "'");
return false;
}
identity_ = resolved.value();
if (!identity_.x_enabled) {
mysqlx_send_error(client_fd_, 1045,
"X Protocol access not enabled for user '" + username + "'");
return false;
}
if (identity_.require_tls) {
mysqlx_send_error(client_fd_, 1045,
"User '" + username + "' requires TLS for X Protocol access");
return false;
}
if (!is_auth_method_allowed("MYSQL41", identity_.allowed_auth_methods)) {
mysqlx_send_error(client_fd_, 1251,
"Authentication method 'MYSQL41' not allowed for user '" + username + "'");
return false;
}
if (identity_.backend_auth_mode == MysqlxBackendAuthMode::pass_through) {
mysqlx_send_error(client_fd_, 1045,
"pass_through backend auth mode not supported in Phase 1");
return false;
}
// Send AuthenticateContinue with challenge.
if (!send_auth_continue_challenge()) {
return false;
}
// Wait for AuthenticateContinue from client with scrambled response.
return handle_authenticate(config_store);
}
mysqlx_send_error(client_fd_, 1251, "Unsupported auth method");
return false;
}
case Mysqlx::ClientMessages_Type_CON_CLOSE:
return false;
default:
mysqlx_send_error(client_fd_, 5000,
"Unexpected message during handshake");
return false;
}
}
}
bool MysqlxFrontendSession::handle_capabilities_get() {
return send_capabilities();
}
bool MysqlxFrontendSession::handle_capabilities_set(const std::vector<uint8_t>& payload) {
Mysqlx::Connection::CapabilitiesSet cap_set;
if (!cap_set.ParseFromArray(payload.data(), static_cast<int>(payload.size()))) {
mysqlx_send_error(client_fd_, 5001, "Invalid CapabilitiesSet message");
return false;
}
// Phase 1: reject TLS requests explicitly since we don't implement it yet.
if (cap_set.has_capabilities()) {
for (const auto& cap : cap_set.capabilities().capabilities()) {
if (cap.name() == "tls") {
mysqlx_send_error(client_fd_, 5001,
"TLS capability not supported by mysqlx plugin in Phase 1");
return false;
}
}
}
return mysqlx_send_ok(client_fd_);
}
bool MysqlxFrontendSession::send_capabilities() {
Mysqlx::Connection::Capabilities caps;
// Advertise supported auth methods.
auto* cap_auth = caps.add_capabilities();
cap_auth->set_name("authentication.mechanisms");
auto* auth_array = cap_auth->mutable_value()->mutable_array();
auto* mysql41 = auth_array->add_value();
mysql41->set_type(Mysqlx::Datatypes::Any::SCALAR);
mysql41->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_STRING);
mysql41->mutable_scalar()->mutable_v_string()->set_value("MYSQL41");
auto* plain = auth_array->add_value();
plain->set_type(Mysqlx::Datatypes::Any::SCALAR);
plain->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_STRING);
plain->mutable_scalar()->mutable_v_string()->set_value("PLAIN");
std::string serialized;
caps.SerializeToString(&serialized);
auto frame = mysqlx_build_frame(
Mysqlx::ServerMessages_Type_CONN_CAPABILITIES,
serialized
);
return mysqlx_write_all(client_fd_, frame.data(), frame.size());
}
bool MysqlxFrontendSession::send_auth_continue_challenge() {
Mysqlx::Session::AuthenticateContinue auth_continue;
auth_continue.set_auth_data(
std::string(auth_challenge_.begin(), auth_challenge_.end())
);
std::string serialized;
auth_continue.SerializeToString(&serialized);
auto frame = mysqlx_build_frame(
Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_CONTINUE,
serialized
);
return mysqlx_write_all(client_fd_, frame.data(), frame.size());
}
bool MysqlxFrontendSession::handle_authenticate(MysqlxConfigStore& /* config_store */) {
// Read AuthenticateContinue from client.
MysqlxFrameHeader header {};
std::vector<uint8_t> payload {};
if (!mysqlx_read_frame(client_fd_, header, payload)) {
return false;
}
if (header.message_type != Mysqlx::ClientMessages_Type_SESS_AUTHENTICATE_CONTINUE) {
mysqlx_send_error(client_fd_, 1045, "Expected AuthenticateContinue");
return false;
}
Mysqlx::Session::AuthenticateContinue auth_continue;
if (!auth_continue.ParseFromArray(payload.data(), static_cast<int>(payload.size()))) {
mysqlx_send_error(client_fd_, 1045, "Invalid AuthenticateContinue");
return false;
}
// Client response is formatted as: schema\0username\0*HEX(scramble)
// Parse out the hex scramble after the '*' marker.
const std::string& response_str = auth_continue.auth_data();
auto star_pos = response_str.find('*');
if (star_pos == std::string::npos || star_pos + 1 >= response_str.size()) {
mysqlx_send_error(client_fd_, 1045, "Invalid MYSQL41 response format");
return false;
}
std::string hex_part = response_str.substr(star_pos + 1);
std::vector<uint8_t> client_response {};
if (!mysqlx_hex_decode(hex_part, client_response)) {
mysqlx_send_error(client_fd_, 1045, "Invalid MYSQL41 hex scramble");
return false;
}
// Verify MYSQL41 scramble against the stored password.
if (!mysqlx_mysql41_verify(auth_challenge_, client_response, identity_.backend_password)) {
mysqlx_send_error(client_fd_, 1045,
"Access denied for user '" + identity_.username + "'");
return false;
}
// Authentication succeeded — send AuthenticateOk.
Mysqlx::Session::AuthenticateOk auth_ok;
std::string serialized;
auth_ok.SerializeToString(&serialized);
auto frame = mysqlx_build_frame(
Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_OK,
serialized
);
return mysqlx_write_all(client_fd_, frame.data(), frame.size());
}

@ -123,7 +123,6 @@ handler_again:
case WAITING_CLIENT_XMSG: handler_waiting_client_msg(); break;
case CONNECTING_SERVER: handler_connecting_server(); break;
case WAITING_SERVER_XMSG: handler_waiting_server_msg(); break;
case X_FAST_FORWARD: handler_fast_forward(); break;
case X_TLS_ACCEPT_INIT: handler_tls_accept_init(); break;
case X_SESSION_RESET_WAITING: handler_session_reset_waiting(); break;
case X_SESSION_CLOSING: handler_session_closing(); break;
@ -641,9 +640,6 @@ void MysqlxSession::handler_waiting_server_msg() {
}
}
void MysqlxSession::handler_fast_forward() {
}
void MysqlxSession::handler_session_reset_waiting() {
if (server_ds().get_fd() < 0) {
return_backend_to_pool();

Loading…
Cancel
Save