Merge branch '2.2' into develop

This commit is contained in:
Markus Mäkelä 2018-08-28 16:06:23 +03:00
commit 9a6f1b2044
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
9 changed files with 137 additions and 64 deletions

View File

@ -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

View File

@ -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`

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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.");

View File

@ -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));

View File

@ -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;

View File

@ -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;