From c5345d34cacff0cc033e9d348a1560e14caa6b6f Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 12 Mar 2018 11:02:11 +0200 Subject: [PATCH] MXS-1714 Use local_address also with MaxScale connections If local address has been specified, then all connections created using mxs_mysql_real_connect() will use that same local address as well. A system test has not been created as our VMs do not have more than one usable IP-address. Locally it has been verified to work as expected. --- server/core/mysql_utils.c | 12 ++ server/core/test/CMakeLists.txt | 2 + server/core/test/test_local_address.cc | 183 +++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 server/core/test/test_local_address.cc diff --git a/server/core/mysql_utils.c b/server/core/mysql_utils.c index ac467d602..0fc7844f8 100644 --- a/server/core/mysql_utils.c +++ b/server/core/mysql_utils.c @@ -163,6 +163,18 @@ MYSQL *mxs_mysql_real_connect(MYSQL *con, SERVER *server, const char *user, cons mysql_optionsv(con, MYSQL_OPT_RECONNECT, &yes); mysql_optionsv(con, MYSQL_INIT_COMMAND, "SET SQL_MODE=''"); + MXS_CONFIG* config = config_get_global_options(); + + if (config->local_address) + { + if (mysql_optionsv(con, MYSQL_OPT_BIND, config->local_address) != 0) + { + MXS_ERROR("'local_address' specified in configuration file, but could not " + "configure MYSQL handle. MaxScale will try to connect using default " + "address."); + } + } + MYSQL* mysql = mysql_real_connect(con, server->name, user, passwd, NULL, server->port, NULL, 0); if (mysql) diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index f9fc7930a..9d3b4de98 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -4,6 +4,7 @@ add_executable(test_dcb testdcb.c) add_executable(test_filter testfilter.c) add_executable(test_hash testhash.c) add_executable(test_hint testhint.c) +add_executable(test_local_address test_local_address.cc) add_executable(test_log testlog.c) add_executable(test_logorder testlogorder.c) add_executable(test_logthrottling testlogthrottling.cc) @@ -28,6 +29,7 @@ target_link_libraries(test_dcb maxscale-common) target_link_libraries(test_filter maxscale-common) target_link_libraries(test_hash maxscale-common) target_link_libraries(test_hint maxscale-common) +target_link_libraries(test_local_address maxscale-common) target_link_libraries(test_log maxscale-common) target_link_libraries(test_logorder maxscale-common) target_link_libraries(test_logthrottling maxscale-common) diff --git a/server/core/test/test_local_address.cc b/server/core/test/test_local_address.cc new file mode 100644 index 000000000..b3d03a125 --- /dev/null +++ b/server/core/test/test_local_address.cc @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include +#include +#include +#include +#include + +using namespace std; + +char USAGE[] = + "usage: test_local_address -u user [-p password] [-a address] [-h host] [-s success]\n" + "\n" + "user : The user to connect as.\n" + "password: The password of the user, default none.\n" + "address : The address to connect from, default none specified.\n" + "host : The address of the host to connect to, default 127.0.0.1.\n" + "success : (0|1), whether the connection attempt is expected to succeed or not, defaul 1.\n" + "\n" + "Example:\n" + "\n" + "MariaDB [(none)]> create user 'l1'@'192.168.1.254';\n" + "MariaDB [(none)]> create user 'l2'@'127.0.0.1';\n" + "\n" + "$ ./test_local_address -s 1 -u l1 -a 192.168.1.254\n" + "User : l1\n" + "Password: (none)\n" + "Server : 127.0.0.1\n" + "Address : 192.168.1.254\n" + "Success : 1\n" + "\n" + "Could connect, as expected.\n" + "$ ./test_local_address -s 0 -u l1 -a 127.0.0.1\n" + "User : l1\n" + "Password: (none)\n" + "Server : 127.0.0.1\n" + "Address : 127.0.0.1\n" + "Success : 0\n" + "\n" + "Could not connect, as expected. " + "Reported error: Access denied for user 'l1'@'localhost' (using password: NO)\n" + "$ ./test_local_address -s 1 -u l2 -a 127.0.0.1\n" + "User : l2\n" + "Password: (none)\n" + "Server : 127.0.0.1\n" + "Address : 127.0.0.1\n" + "Success : 1\n" + "\n" + "Could connect, as expected.\n" + "$ ./test_local_address -s 0 -u l2 -a 192.168.1.254\n" + "User : l2\n" + "Password: (none)\n" + "Server : 127.0.0.1\n" + "Address : 192.168.1.254\n" + "Success : 0\n" + "\n" + "Could not connect, as expected. " + "Reported error: Access denied for user 'l2'@'192.168.1.254' (using password: NO)\n"; + +namespace +{ + +int test(bool success, const char* zHost, const char* zUser, const char* zPassword, const char* zAddress) +{ + int rv = EXIT_FAILURE; + + MXS_CONFIG* config = config_get_global_options(); + config->local_address = const_cast(zAddress); + + SERVER server; + memset(&server, 0, sizeof(server)); + + strcpy(server.name, zHost); + server.port = 3306; + + MYSQL* pMysql = mysql_init(NULL); + + MYSQL* pConn = mxs_mysql_real_connect(pMysql, &server, zUser, zPassword); + + if (pConn) + { + if (success) + { + cout << "Could connect, as expected." << endl; + rv = EXIT_SUCCESS; + } + else + { + cerr << "Error: Connection succeeded, although expected not to." << endl; + } + + mysql_close(pConn); + } + else + { + if (!success) + { + cout << "Could not connect, as expected. Reported error: " << mysql_error(pMysql) << endl; + rv = EXIT_SUCCESS; + } + else + { + cerr << "Error: " << mysql_error(pMysql) << endl; + } + + mysql_close(pMysql); + } + + return rv; +} + +} + +int main(int argc, char* argv[]) +{ + int rv = EXIT_SUCCESS; + + int opt; + + const char* zUser = NULL; + const char* zPassword = NULL; + const char* zAddress = NULL; + const char* zHost = "127.0.0.1"; + bool success = true; + + while ((opt = getopt(argc, argv, "a:h:p:s:u:")) != -1) + { + switch (opt) + { + case 'a': + zAddress = optarg; + break; + + case 'h': + zHost = optarg; + break; + + case 'p': + zPassword = optarg; + break; + + case 's': + success = (atoi(optarg) == 0) ? false : true; + break; + + case 'u': + zUser = optarg; + break; + + default: + rv = EXIT_FAILURE; + } + } + + if ((rv == EXIT_SUCCESS) && zUser) + { + cout << "User : " << zUser << endl; + cout << "Password: " << (zPassword ? zPassword : "(none)") << endl; + cout << "Server : " << zHost << endl; + cout << "Address : " << (zAddress ? zAddress : "(default)") << endl; + cout << "Success : " << success << endl; + cout << endl; + + rv = test(success, zHost, zUser, zPassword, zAddress); + } + else + { + cerr << USAGE << endl; + } + + return rv; +}