From 29932f7fc8b331f97114d5923bb834446852cdcc Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Wed, 7 May 2014 18:08:28 +0200 Subject: [PATCH] MySQL handshake has a configurable version_string MySQL handshake has no a configurable version_string parameter in service section. The default value is the server version of the embedded mysqld library. Set option example: version_string=5.5.37-MariaDB-log Default is similar to: 5.5.35-MariaDB --- server/core/config.c | 47 +++++++++ server/core/service.c | 4 +- server/include/config.h | 8 +- server/include/gateway_mysql.h | 126 ------------------------- server/include/service.h | 2 + server/modules/protocol/mysql_client.c | 19 +++- 6 files changed, 73 insertions(+), 133 deletions(-) delete mode 100644 server/include/gateway_mysql.h diff --git a/server/core/config.c b/server/core/config.c index d964ba5fa..4124a8837 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -29,6 +29,7 @@ * 06/02/14 Massimiliano Pinto Added support for enable/disable root user in services * 14/02/14 Massimiliano Pinto Added enable_root_user in the service_params list * 11/03/14 Massimiliano Pinto Added Unix socket support + * 11/05/14 Massimiliano Pinto Added version_string support to service * * @endverbatim */ @@ -43,6 +44,7 @@ #include #include #include +#include extern int lm_enabled_logfiles_bitmask; @@ -56,6 +58,7 @@ static void check_config_objects(CONFIG_CONTEXT *context); static char *config_file = NULL; static GATEWAY_CONF gateway; +char *version_string = NULL; /** * Config item handler for the ini file reader @@ -116,6 +119,21 @@ config_load(char *file) CONFIG_CONTEXT config; int rval; + MYSQL *conn; + conn = mysql_init(NULL); + if (conn) { + if (mysql_real_connect(conn, NULL, NULL, NULL, NULL, 0, NULL, 0)) { + char *ptr; + version_string = (char *)mysql_get_server_info(conn); + ptr = strstr(version_string, "-embedded"); + if (ptr) { + *ptr = '\0'; + } + + } + mysql_close(conn); + } + global_defaults(); config.object = ""; @@ -146,6 +164,11 @@ int rval; if (!config_file) return 0; + + + if (gateway.version_string) + free(gateway.version_string); + global_defaults(); config.object = ""; @@ -202,6 +225,15 @@ int error_count = 0; config_get_value(obj->parameters, "passwd"); char *enable_root_user = config_get_value(obj->parameters, "enable_root_user"); + + char *version_string = config_get_value(obj->parameters, "version_string"); + + if (version_string) { + ((SERVICE *)(obj->element))->version_string = strdup(version_string); + } else { + if (gateway.version_string) + ((SERVICE *)(obj->element))->version_string = strdup(gateway.version_string); + } if (enable_root_user) serviceEnableRootUser(obj->element, atoi(enable_root_user)); @@ -580,6 +612,10 @@ static void global_defaults() { gateway.n_threads = 1; + if (version_string != NULL) + gateway.version_string = strdup(version_string); + else + gateway.version_string = NULL; } /** @@ -622,6 +658,7 @@ SERVER *server; char *user; char *auth; char *enable_root_user; + char *version_string; enable_root_user = config_get_value(obj->parameters, "enable_root_user"); @@ -629,6 +666,15 @@ SERVER *server; "user"); auth = config_get_value(obj->parameters, "passwd"); + + version_string = config_get_value(obj->parameters, "version_string"); + + if (version_string) { + if (service->version_string) + free(service->version_string); + service->version_string = strdup(version_string); + } + if (user && auth) { service_update(service, router, user, @@ -861,6 +907,7 @@ static char *service_params[] = "user", "passwd", "enable_root_user", + "version_string", NULL }; diff --git a/server/core/service.c b/server/core/service.c index db41f881a..5cd2916e1 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -28,6 +28,7 @@ * 06/02/14 Massimiliano Pinto Added: serviceEnableRootUser routine * 25/02/14 Massimiliano Pinto Added: service refresh limit feature * 28/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services) + * 07/05/14 Massimiliano Pinto Added: version_string initialized to NULL * * @endverbatim */ @@ -75,6 +76,7 @@ SERVICE *service; } service->name = strdup(servname); service->routerModule = strdup(router); + service->version_string = NULL; memset(&service->stats, 0, sizeof(SERVICE_STATS)); service->ports = NULL; service->stats.started = time(0); @@ -159,7 +161,7 @@ GWPROTOCOL *funcs; if (port->listener->func.listen(port->listener, config_bind)) { port->listener->session = session_alloc(service, port->listener); - + if (port->listener->session != NULL) { port->listener->session->state = SESSION_STATE_LISTENER; listeners += 1; diff --git a/server/include/config.h b/server/include/config.h index 88620f015..03da9b029 100644 --- a/server/include/config.h +++ b/server/include/config.h @@ -24,8 +24,9 @@ * @verbatim * Revision History * - * Date Who Description - * 21/06/13 Mark Riddoch Initial implementation + * Date Who Description + * 21/06/13 Mark Riddoch Initial implementation + * 07/05/14 Massimiliano Pinto Added version_string to global configuration * * @endverbatim */ @@ -54,7 +55,8 @@ typedef struct config_context { * The gateway global configuration data */ typedef struct { - int n_threads; /**< Number of polling threads */ + int n_threads; /**< Number of polling threads */ + char *version_string; /**< The version string of embedded database library */ } GATEWAY_CONF; extern int config_load(char *); diff --git a/server/include/gateway_mysql.h b/server/include/gateway_mysql.h deleted file mode 100644 index 01c569bba..000000000 --- a/server/include/gateway_mysql.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This file is distributed as part of the SkySQL Gateway. It is free - * software: you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation, - * version 2. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright SkySQL Ab 2013 - * - */ - -/* - * MYSQL mysql protocol header file - * Revision History - * - * Date Who Description - * 10/06/13 Massimiliano Pinto Initial implementation - * - */ -#include - -/* Protocol packing macros. */ -#define gw_mysql_set_byte2(__buffer, __int) do { \ - (__buffer)[0]= (uint8_t)((__int) & 0xFF); \ - (__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); } while (0) -#define gw_mysql_set_byte3(__buffer, __int) do { \ - (__buffer)[0]= (uint8_t)((__int) & 0xFF); \ - (__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \ - (__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); } while (0) -#define gw_mysql_set_byte4(__buffer, __int) do { \ - (__buffer)[0]= (uint8_t)((__int) & 0xFF); \ - (__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \ - (__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); \ - (__buffer)[3]= (uint8_t)(((__int) >> 24) & 0xFF); } while (0) - - -/* Protocol unpacking macros. */ -#define gw_mysql_get_byte2(__buffer) \ - (uint16_t)((__buffer)[0] | \ - ((__buffer)[1] << 8)) -#define gw_mysql_get_byte3(__buffer) \ - (uint32_t)((__buffer)[0] | \ - ((__buffer)[1] << 8) | \ - ((__buffer)[2] << 16)) -#define gw_mysql_get_byte4(__buffer) \ - (uint32_t)((__buffer)[0] | \ - ((__buffer)[1] << 8) | \ - ((__buffer)[2] << 16) | \ - ((__buffer)[3] << 24)) -#define gw_mysql_get_byte8(__buffer) \ - ((uint64_t)(__buffer)[0] | \ - ((uint64_t)(__buffer)[1] << 8) | \ - ((uint64_t)(__buffer)[2] << 16) | \ - ((uint64_t)(__buffer)[3] << 24) | \ - ((uint64_t)(__buffer)[4] << 32) | \ - ((uint64_t)(__buffer)[5] << 40) | \ - ((uint64_t)(__buffer)[6] << 48) | \ - ((uint64_t)(__buffer)[7] << 56)) - -typedef enum -{ - GW_MYSQL_CAPABILITIES_NONE= 0, - GW_MYSQL_CAPABILITIES_LONG_PASSWORD= (1 << 0), - GW_MYSQL_CAPABILITIES_FOUND_ROWS= (1 << 1), - GW_MYSQL_CAPABILITIES_LONG_FLAG= (1 << 2), - GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB= (1 << 3), - GW_MYSQL_CAPABILITIES_NO_SCHEMA= (1 << 4), - GW_MYSQL_CAPABILITIES_COMPRESS= (1 << 5), - GW_MYSQL_CAPABILITIES_ODBC= (1 << 6), - GW_MYSQL_CAPABILITIES_LOCAL_FILES= (1 << 7), - GW_MYSQL_CAPABILITIES_IGNORE_SPACE= (1 << 8), - GW_MYSQL_CAPABILITIES_PROTOCOL_41= (1 << 9), - GW_MYSQL_CAPABILITIES_INTERACTIVE= (1 << 10), - GW_MYSQL_CAPABILITIES_SSL= (1 << 11), - GW_MYSQL_CAPABILITIES_IGNORE_SIGPIPE= (1 << 12), - GW_MYSQL_CAPABILITIES_TRANSACTIONS= (1 << 13), - GW_MYSQL_CAPABILITIES_RESERVED= (1 << 14), - GW_MYSQL_CAPABILITIES_SECURE_CONNECTION= (1 << 15), - GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS= (1 << 16), - GW_MYSQL_CAPABILITIES_MULTI_RESULTS= (1 << 17), - GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS= (1 << 18), - GW_MYSQL_CAPABILITIES_PLUGIN_AUTH= (1 << 19), - GW_MYSQL_CAPABILITIES_SSL_VERIFY_SERVER_CERT= (1 << 30), - GW_MYSQL_CAPABILITIES_REMEMBER_OPTIONS= (1 << 31), - GW_MYSQL_CAPABILITIES_CLIENT= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD | - GW_MYSQL_CAPABILITIES_FOUND_ROWS | - GW_MYSQL_CAPABILITIES_LONG_FLAG | - GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB | - GW_MYSQL_CAPABILITIES_LOCAL_FILES | - GW_MYSQL_CAPABILITIES_PLUGIN_AUTH | - GW_MYSQL_CAPABILITIES_TRANSACTIONS | - GW_MYSQL_CAPABILITIES_PROTOCOL_41 | - GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS | - GW_MYSQL_CAPABILITIES_MULTI_RESULTS | - GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS | - GW_MYSQL_CAPABILITIES_SECURE_CONNECTION), - GW_MYSQL_CAPABILITIES_CLIENT_COMPRESS= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD | - GW_MYSQL_CAPABILITIES_FOUND_ROWS | - GW_MYSQL_CAPABILITIES_LONG_FLAG | - GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB | - GW_MYSQL_CAPABILITIES_LOCAL_FILES | - GW_MYSQL_CAPABILITIES_PLUGIN_AUTH | - GW_MYSQL_CAPABILITIES_TRANSACTIONS | - GW_MYSQL_CAPABILITIES_PROTOCOL_41 | - GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS | - GW_MYSQL_CAPABILITIES_MULTI_RESULTS | - GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS | - GW_MYSQL_CAPABILITIES_COMPRESS - ), -} gw_mysql_capabilities_t; - - -#define SMALL_CHUNK 1024 -#define MAX_CHUNK SMALL_CHUNK * 8 * 4 -#define ToHex(Y) (Y>='0'&&Y<='9'?Y-'0':Y-'A'+10) - -extern int mysql_send_ok(DCB *, int, int, const char *); -extern int MySQLSendHandshake(DCB *); diff --git a/server/include/service.h b/server/include/service.h index d52a7eccf..0a8db9ea2 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -38,6 +38,7 @@ * 23/06/13 Mark Riddoch Added service user and users * 06/02/14 Massimiliano Pinto Added service flag for root user access * 25/02/14 Massimiliano Pinto Added service refresh limit feature + * 07/05/14 Massimiliano Pinto Added version_string field to service struct * * @endverbatim */ @@ -108,6 +109,7 @@ typedef struct service { *router; /**< The router we are using */ void *router_instance; /**< The router instance for this service */ + char *version_string; /** version string for this service listeners */ struct server *databases; /**< The set of servers in the backend */ SERVICE_USER credentials; /**< The cedentials of the service user */ SPINLOCK spin; /**< The service spinlock */ diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 5bd20f4f9..5e4c9bb21 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -33,6 +33,7 @@ * If current user is authenticated the new users' table will replace the old one * 28/02/2014 Massimiliano Pinto Added: client IPv4 in dcb->ipv4 and inet_ntop for string representation * 11/03/2014 Massimiliano Pinto Added: Unix socket support + * 07/05/2014 Massimiliano Pinto Added: specific version string in server handshake * */ @@ -225,10 +226,21 @@ MySQLSendHandshake(DCB* dcb) uint8_t mysql_filler_ten[10]; uint8_t mysql_last_byte = 0x00; char server_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1]=""; + char *version_string; + int len_version_string=0; MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol); GWBUF *buf; + /* get the version string from service property if available*/ + if (dcb->service->version_string != NULL) { + version_string = dcb->service->version_string; + len_version_string = strlen(version_string); + } else { + version_string = GW_MYSQL_VERSION; + len_version_string = strlen(GW_MYSQL_VERSION); + } + gw_generate_random_str(server_scramble, GW_MYSQL_SCRAMBLE_SIZE); // copy back to the caller @@ -245,7 +257,7 @@ MySQLSendHandshake(DCB* dcb) memcpy(mysql_plugin_data, server_scramble + 8, 12); - mysql_payload_size = sizeof(mysql_protocol_version) + (strlen(GW_MYSQL_VERSION) + 1) + sizeof(mysql_thread_id) + 8 + sizeof(mysql_filler) + sizeof(mysql_server_capabilities_one) + sizeof(mysql_server_language) + sizeof(mysql_server_status) + sizeof(mysql_server_capabilities_two) + sizeof(mysql_scramble_len) + sizeof(mysql_filler_ten) + 12 + sizeof(mysql_last_byte) + strlen("mysql_native_password") + sizeof(mysql_last_byte); + mysql_payload_size = sizeof(mysql_protocol_version) + (len_version_string + 1) + sizeof(mysql_thread_id) + 8 + sizeof(mysql_filler) + sizeof(mysql_server_capabilities_one) + sizeof(mysql_server_language) + sizeof(mysql_server_status) + sizeof(mysql_server_capabilities_two) + sizeof(mysql_scramble_len) + sizeof(mysql_filler_ten) + 12 + sizeof(mysql_last_byte) + strlen("mysql_native_password") + sizeof(mysql_last_byte); // allocate memory for packet header + payload if ((buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size)) == NULL) @@ -271,8 +283,9 @@ MySQLSendHandshake(DCB* dcb) mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_protocol_version); // write server version plus 0 filler - strcpy((char *)mysql_handshake_payload, GW_MYSQL_VERSION); - mysql_handshake_payload = mysql_handshake_payload + strlen(GW_MYSQL_VERSION); + strcpy((char *)mysql_handshake_payload, version_string); + mysql_handshake_payload = mysql_handshake_payload + len_version_string; + *mysql_handshake_payload = 0x00; mysql_handshake_payload++;