Merge branch '2.2' into develop
This commit is contained in:
commit
9a6f1b2044
@ -12,6 +12,8 @@ a routing hint to all following statements. This routing hint guides the routing
|
||||
module to route the statement to the master server where data is guaranteed to
|
||||
be in an up-to-date state.
|
||||
|
||||
### Controlling the Filter with SQL Comments
|
||||
|
||||
The triggering of the filter can be limited further by adding MaxScale supported
|
||||
comments to queries and/or by using regular expressions. The query comments take
|
||||
precedence: if a comment is found it is obayed even if a regular expression
|
||||
|
@ -1534,8 +1534,20 @@ to `true` and provide the three files for `ssl_cert`, `ssl_key` and
|
||||
|
||||
After this, MaxScale connections between the server and/or the client will be
|
||||
encrypted. Note that the database must be configured to use TLS/SSL connections
|
||||
if backend connection encryption is used. When client-side encryption is
|
||||
enabled, only encrypted connections to MaxScale can be created.
|
||||
if backend connection encryption is used.
|
||||
|
||||
**Note:** MaxScale does not allow mixed use of TLS/SSL and normal connections on
|
||||
the same port.
|
||||
|
||||
If TLS encryption is enabled for a listener, any unencrypted connections to it
|
||||
will be rejected. MaxScale does this to improve security by preventing
|
||||
accidental creation on unencrypted connections.
|
||||
|
||||
The separation of secure and insecure connections differs from the MariaDB
|
||||
server which allows both secure and insecure connections on the same port. As
|
||||
MaxScale is the gateway through which all connections go, in order to guarantee
|
||||
a more secure system MaxScale enforces a stricter security policy than what the
|
||||
server does.
|
||||
|
||||
#### `ssl`
|
||||
|
||||
|
@ -318,7 +318,6 @@ MAXAVRO_FILE* maxavro_file_open(const char* filename)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to initialize avrofile.");
|
||||
maxavro_schema_free(avrofile->schema);
|
||||
error = true;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <poll.h>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <iterator>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
@ -121,7 +122,7 @@ public:
|
||||
|
||||
Closer(T t):
|
||||
m_t(t),
|
||||
m_close(false)
|
||||
m_close(true)
|
||||
{
|
||||
}
|
||||
|
||||
@ -185,7 +186,6 @@ Connection::Connection(const std::string& address,
|
||||
m_timeout(timeout),
|
||||
m_connected(false)
|
||||
{
|
||||
m_buf_ptr = m_buffer.begin();
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
@ -276,7 +276,7 @@ bool Connection::connect(const std::string& table, const std::string& gtid)
|
||||
m_error = "Failed to write request: ";
|
||||
m_error += strerror_r(errno, err, sizeof(err));
|
||||
}
|
||||
else if ((m_first_row = read()))
|
||||
else if (read_schema())
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
@ -391,18 +391,13 @@ SRow Connection::process_row(json_t* js)
|
||||
return rval;
|
||||
}
|
||||
|
||||
SRow Connection::read()
|
||||
bool Connection::read_schema()
|
||||
{
|
||||
m_error.clear();
|
||||
SRow rval;
|
||||
bool rval = false;
|
||||
std::string row;
|
||||
|
||||
if (m_first_row)
|
||||
{
|
||||
rval.swap(m_first_row);
|
||||
assert(!m_first_row);
|
||||
}
|
||||
else if (read_row(row))
|
||||
if (read_row(row))
|
||||
{
|
||||
json_error_t err;
|
||||
json_t* js = json_loads(row.c_str(), JSON_ALLOW_NUL, &err);
|
||||
@ -413,11 +408,7 @@ SRow Connection::read()
|
||||
{
|
||||
m_schema = row;
|
||||
process_schema(js);
|
||||
rval = Connection::read();
|
||||
}
|
||||
else
|
||||
{
|
||||
rval = process_row(js);
|
||||
rval = true;
|
||||
}
|
||||
|
||||
json_decref(js);
|
||||
@ -429,6 +420,40 @@ SRow Connection::read()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_error == CDC::TIMEOUT)
|
||||
{
|
||||
assert(rval == false);
|
||||
m_error += ". Data received so far: '";
|
||||
std::copy(m_buffer.begin(), m_buffer.end(), std::back_inserter(m_error));
|
||||
m_error += "'";
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
SRow Connection::read()
|
||||
{
|
||||
m_error.clear();
|
||||
SRow rval;
|
||||
std::string row;
|
||||
|
||||
if (read_row(row))
|
||||
{
|
||||
json_error_t err;
|
||||
json_t* js = json_loads(row.c_str(), JSON_ALLOW_NUL, &err);
|
||||
|
||||
if (js)
|
||||
{
|
||||
rval = process_row(js);
|
||||
json_decref(js);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_error = "Failed to parse JSON: ";
|
||||
m_error += err.text;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
@ -517,14 +542,14 @@ bool Connection::do_registration()
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool Connection::is_error(const char* str)
|
||||
bool Connection::is_error()
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
if (str[0] == 'E' && str[1] == 'R' && str[2] == 'R')
|
||||
if (m_buffer.size() >= 3 && m_buffer[0] == 'E' && m_buffer[1] == 'R' && m_buffer[2] == 'R')
|
||||
{
|
||||
m_error = "MaxScale responded with an error: ";
|
||||
m_error += str;
|
||||
m_error.append(m_buffer.begin(), m_buffer.end());
|
||||
rval = true;
|
||||
}
|
||||
|
||||
@ -539,11 +564,19 @@ bool Connection::read_row(std::string& dest)
|
||||
{
|
||||
if (!m_buffer.empty())
|
||||
{
|
||||
std::vector<char>::iterator it = std::find(m_buf_ptr, m_buffer.end(), '\n');
|
||||
if (is_error())
|
||||
{
|
||||
rval = false;
|
||||
break;
|
||||
}
|
||||
|
||||
std::deque<char>::iterator it = std::find(m_buffer.begin(), m_buffer.end(), '\n');
|
||||
|
||||
if (it != m_buffer.end())
|
||||
{
|
||||
dest.assign(m_buf_ptr, it);
|
||||
m_buf_ptr = it + 1;
|
||||
dest.assign(m_buffer.begin(), it);
|
||||
m_buffer.erase(m_buffer.begin(), std::next(it));
|
||||
assert(m_buffer.empty() || m_buffer[0] != '\n');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -566,27 +599,14 @@ bool Connection::read_row(std::string& dest)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_connected)
|
||||
{
|
||||
// This is here to work around a missing newline in MaxScale error messages
|
||||
buf[rc] = '\0';
|
||||
|
||||
if (is_error(buf))
|
||||
{
|
||||
rval = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer.erase(m_buffer.begin(), m_buf_ptr);
|
||||
assert(std::find(m_buffer.begin(), m_buffer.end(), '\n') == m_buffer.end());
|
||||
m_buffer.insert(m_buffer.end(), buf, buf + rc);
|
||||
m_buf_ptr = m_buffer.begin();
|
||||
}
|
||||
std::copy(buf, buf + rc, std::back_inserter(m_buffer));
|
||||
|
||||
if (!m_connected && is_error(dest.c_str()))
|
||||
{
|
||||
rval = false;
|
||||
if (is_error())
|
||||
{
|
||||
rval = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <string>
|
||||
#include <tr1/memory>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <jansson.h>
|
||||
@ -137,17 +138,17 @@ private:
|
||||
SValueVector m_keys;
|
||||
SValueVector m_types;
|
||||
int m_timeout;
|
||||
std::vector<char> m_buffer;
|
||||
std::vector<char>::iterator m_buf_ptr;
|
||||
std::deque<char> m_buffer;
|
||||
SRow m_first_row;
|
||||
bool m_connected;
|
||||
|
||||
bool do_auth();
|
||||
bool do_registration();
|
||||
bool read_row(std::string& dest);
|
||||
bool read_schema();
|
||||
void process_schema(json_t* json);
|
||||
SRow process_row(json_t*);
|
||||
bool is_error(const char* str);
|
||||
bool is_error();
|
||||
|
||||
// Lower-level functions
|
||||
int wait_for_event(short events);
|
||||
|
@ -24,13 +24,13 @@ void reset_replication(TestConnections& test)
|
||||
{
|
||||
int ind = master_id - 1;
|
||||
replicate_from(test, 0, ind);
|
||||
test.maxscales->wait_for_monitor();
|
||||
test.maxscales->wait_for_monitor(2);
|
||||
get_output(test);
|
||||
int ec;
|
||||
stringstream switchover;
|
||||
switchover << "maxadmin call command mysqlmon switchover MySQL-Monitor server1 server" << master_id;
|
||||
test.maxscales->ssh_node_output(0, switchover.str().c_str() , true, &ec);
|
||||
test.maxscales->wait_for_monitor();
|
||||
test.maxscales->wait_for_monitor(2);
|
||||
master_id = get_master_server_id(test);
|
||||
cout << "Master server id is now back to " << master_id << endl;
|
||||
test.assert(master_id == 1, "Switchover back to server1 failed");
|
||||
@ -106,7 +106,7 @@ void check_test_2(TestConnections& test)
|
||||
|
||||
// Reset state
|
||||
replicate_from(test, 1, master_id - 1);
|
||||
test.maxscales->wait_for_monitor();
|
||||
test.maxscales->wait_for_monitor(2);
|
||||
get_output(test);
|
||||
StringSet node_states = test.get_server_status("server2");
|
||||
test.assert(node_states.find("Slave") != node_states.end(), "Server 2 is not replicating.");
|
||||
@ -143,7 +143,7 @@ void prepare_test_3(TestConnections& test)
|
||||
test.repl->start_node(2, (char *) "");
|
||||
test.repl->start_node(3, (char *) "");
|
||||
test.maxscales->start_maxscale(0);
|
||||
test.maxscales->wait_for_monitor();
|
||||
test.maxscales->wait_for_monitor(2);
|
||||
|
||||
test.repl->connect();
|
||||
test.tprintf("Settings changed.");
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <limits.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/stat.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -1545,21 +1546,30 @@ static bool reauthenticate_client(MXS_SESSION* session, GWBUF* packetbuf)
|
||||
|
||||
if (session->client_dcb->authfunc.reauthenticate)
|
||||
{
|
||||
uint64_t payloadlen = gwbuf_length(packetbuf) - MYSQL_HEADER_LEN;
|
||||
MySQLProtocol* proto = (MySQLProtocol*)session->client_dcb->protocol;
|
||||
uint8_t payload[gwbuf_length(packetbuf) - MYSQL_HEADER_LEN];
|
||||
gwbuf_copy_data(packetbuf, MYSQL_HEADER_LEN, sizeof(payload), payload);
|
||||
std::vector<uint8_t> payload;
|
||||
payload.resize(payloadlen);
|
||||
gwbuf_copy_data(packetbuf, MYSQL_HEADER_LEN, payloadlen, &payload[0]);
|
||||
|
||||
// Will contains extra data but the username is null-terminated
|
||||
char user[gwbuf_length(proto->stored_query) - MYSQL_HEADER_LEN - 1];
|
||||
gwbuf_copy_data(proto->stored_query, MYSQL_HEADER_LEN + 1,
|
||||
sizeof(user), (uint8_t*)user);
|
||||
char user[MYSQL_USER_MAXLEN + 1];
|
||||
gwbuf_copy_data(proto->stored_query, MYSQL_HEADER_LEN + 1, sizeof(user), (uint8_t*)user);
|
||||
|
||||
char* end = user + sizeof(user);
|
||||
|
||||
if (std::find(user, end, '\0') == end)
|
||||
{
|
||||
mysql_send_auth_error(session->client_dcb, 3, 0, "Malformed AuthSwitchRequest packet");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the new username to the session data
|
||||
MYSQL_session* data = (MYSQL_session*)session->client_dcb->data;
|
||||
strcpy(data->user, user);
|
||||
|
||||
int rc = session->client_dcb->authfunc.reauthenticate(session->client_dcb, data->user,
|
||||
payload, sizeof(payload),
|
||||
&payload[0], payload.size(),
|
||||
proto->scramble, sizeof(proto->scramble),
|
||||
data->client_sha1, sizeof(data->client_sha1));
|
||||
|
||||
|
@ -343,17 +343,17 @@ void AvroSession::process_command(GWBUF *queue)
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "ERR NO-FILE File '%s' not found.", avro_binfile.c_str());
|
||||
dcb_printf(dcb, "ERR NO-FILE File '%s' not found.\n", avro_binfile.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "ERR REQUEST-DATA with no data");
|
||||
dcb_printf(dcb, "ERR REQUEST-DATA with no data\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const char err[] = "ERR: Unknown command";
|
||||
const char err[] = "ERR: Unknown command\n";
|
||||
GWBUF *reply = gwbuf_alloc_and_load(sizeof(err), err);
|
||||
dcb->func.write(dcb, reply);
|
||||
}
|
||||
@ -571,8 +571,7 @@ bool AvroSession::stream_data()
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "No file specified\n");
|
||||
dcb_printf(dcb, "ERR avro file not specified");
|
||||
dcb_printf(dcb, "ERR avro file not specified\n");
|
||||
}
|
||||
|
||||
return read_more;
|
||||
|
@ -444,6 +444,29 @@ static bool pos_is_ok(Avro* router, const REP_HEADER& hdr, uint64_t pos)
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool read_fde(Avro* router)
|
||||
{
|
||||
bool rval = false;
|
||||
avro_binlog_end_t rc;
|
||||
REP_HEADER hdr;
|
||||
|
||||
if (read_header(router, 4, &hdr, &rc))
|
||||
{
|
||||
if (GWBUF *result = read_event_data(router, &hdr, 4))
|
||||
{
|
||||
router->handler.handle_event(hdr, GWBUF_DATA(result));
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
else if (rc == AVRO_OK)
|
||||
{
|
||||
// Empty file
|
||||
rval = true;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read all replication events from a binlog file.
|
||||
*
|
||||
@ -457,12 +480,19 @@ static bool pos_is_ok(Avro* router, const REP_HEADER& hdr, uint64_t pos)
|
||||
*/
|
||||
avro_binlog_end_t avro_read_all_events(Avro *router)
|
||||
{
|
||||
mxb_assert(router->binlog_fd != -1);
|
||||
|
||||
if (!read_fde(router))
|
||||
{
|
||||
MXS_ERROR("Failed to read the FDE event from the binary log: %d, %s",
|
||||
errno, mxs_strerror(errno));
|
||||
return AVRO_BINLOG_ERROR;
|
||||
}
|
||||
|
||||
uint64_t pos = router->current_pos;
|
||||
std::string next_binlog;
|
||||
bool rotate_seen = false;
|
||||
|
||||
mxb_assert(router->binlog_fd != -1);
|
||||
|
||||
while (!service_should_stop)
|
||||
{
|
||||
avro_binlog_end_t rc;
|
||||
|
Loading…
x
Reference in New Issue
Block a user