From 8fd63e1e228023ea7fef24f3cd7bc5b2a75975fb Mon Sep 17 00:00:00 2001 From: Shihao Chen Date: Mon, 1 Jun 2020 12:17:13 +0800 Subject: [PATCH 1/4] add C client support for redirection --- include/mysql.h | 9 + libmariadb/mariadb_lib.c | 3 + plugins/connection/CMakeLists.txt | 8 + plugins/connection/redirection.c | 116 +++++ plugins/connection/redirection_utility.c | 555 +++++++++++++++++++++++ plugins/connection/redirection_utility.h | 18 + 6 files changed, 709 insertions(+) create mode 100644 plugins/connection/redirection.c create mode 100644 plugins/connection/redirection_utility.c create mode 100644 plugins/connection/redirection_utility.h diff --git a/include/mysql.h b/include/mysql.h index a160de0a4..d6b59ae65 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -216,6 +216,7 @@ extern const char *SQLSTATE_UNKNOWN; MYSQL_OPT_MAX_ALLOWED_PACKET, MYSQL_OPT_NET_BUFFER_LENGTH, MYSQL_OPT_TLS_VERSION, + MYSQL_OPT_USE_REDIRECTION, /* MariaDB specific */ MYSQL_PROGRESS_CALLBACK=5999, @@ -302,6 +303,13 @@ extern const char *SQLSTATE_UNKNOWN; MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY }; + typedef enum mysql_redirection_mode + { + REDIRECTION_OFF, + REDIRECTION_ON, + REDIRECTION_PREFERRED + } enable_redirect; + struct st_mysql_options { unsigned int connect_timeout, read_timeout, write_timeout; unsigned int port, protocol; @@ -323,6 +331,7 @@ struct st_mysql_options { char *bind_address; my_bool secure_auth; my_bool report_data_truncation; + enable_redirect redirection_mode; /* function pointers for local infile support */ int (*local_infile_init)(void **, const char *, void *); int (*local_infile_read)(void *, char *, unsigned int); diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 8c2a99bf1..ed9911abb 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -3053,6 +3053,9 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) mysql->options.extension->connect_attrs_len= 0; } break; + case MYSQL_OPT_USE_REDIRECTION: + mysql->options.redirection_mode = *(enable_redirect*)arg1; + break; case MARIADB_OPT_CONNECTION_HANDLER: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, connection_handler, (char *)arg1); break; diff --git a/plugins/connection/CMakeLists.txt b/plugins/connection/CMakeLists.txt index cbfd46339..a4d4adfbc 100644 --- a/plugins/connection/CMakeLists.txt +++ b/plugins/connection/CMakeLists.txt @@ -11,3 +11,11 @@ REGISTER_PLUGIN(TARGET replication CONFIGURATIONS STATIC DYNAMIC OFF DEFAULT OFF SOURCES ${CC_SOURCE_DIR}/plugins/connection/replication.c) + +# Redirection +REGISTER_PLUGIN(TARGET redirection + TYPE MARIADB_CLIENT_PLUGIN_CONNECTION + CONFIGURATIONS STATIC DYNAMIC OFF + DEFAULT DYNAMIC + SOURCES ${CC_SOURCE_DIR}/plugins/connection/redirection.c + ${CC_SOURCE_DIR}/plugins/connection/redirection_utility.c) diff --git a/plugins/connection/redirection.c b/plugins/connection/redirection.c new file mode 100644 index 000000000..d418efba5 --- /dev/null +++ b/plugins/connection/redirection.c @@ -0,0 +1,116 @@ +/************************************************************************************ + Copyright (C) 2015-2018 MariaDB Corporation AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ +/* MariaDB Connection plugin for redirection. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "redirection_utility.h" +#ifndef WIN32 +#include +#endif +/* redirection function declaration */ +int redirection_init(char* errbuf, size_t buf_size, int argc, va_list argv); +MYSQL* redirection_connect( + MYSQL* mysql, + const char* host, + const char* user, + const char* passwd, + const char* db, + unsigned int port, + const char* unix_socket, + unsigned long clientflag); +void redirection_close(MYSQL* mysql); +int redirection_set_connection( + MYSQL* mysql, + enum enum_server_command command, + const char* arg, + size_t length, + my_bool skipp_check, + void* opt_arg); + +#ifndef PLUGIN_DYNAMIC +MARIADB_CONNECTION_PLUGIN redirection_client_plugin = +#else +MARIADB_CONNECTION_PLUGIN _mysql_client_plugin_declaration_ = +#endif +{ + MARIADB_CLIENT_CONNECTION_PLUGIN, + MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION, + "redirection", + "Shihao Chen", + "MariaDB connection plugin for redirection", + {1, 0, 0}, + "LGPL", + NULL, + redirection_init, + NULL, + NULL, + redirection_connect, + redirection_close, + NULL, + redirection_set_connection, + NULL, + NULL +}; + +int redirection_init(char* errbuf, size_t buf_size, int argc, va_list argv) { + return init_redirection_cache(); +} + +MYSQL* redirection_connect(MYSQL* mysql, const char* host, const char* user, const char* passwd, + const char* db, unsigned int port, const char* unix_socket, unsigned long clientflag) +{ + struct st_mariadb_api* libmariadb_api = mysql->methods->api; + if (libmariadb_api == NULL) + { + return NULL; + } + + if (check_redirect(mysql, host, user, passwd, db, port, unix_socket, clientflag)) { + return mysql; + } + + return redirect(mysql, host, user, passwd, db, port, unix_socket, clientflag); +} + +void redirection_close(MYSQL* mysql) +{ + if (mysql != NULL) + { + mysql->extension->conn_hdlr->data = NULL; + } +} + +int redirection_set_connection( + MYSQL* mysql, + enum enum_server_command command, + const char* arg, + size_t length, + my_bool skipp_check, + void* opt_arg) +{ + return 0; +} \ No newline at end of file diff --git a/plugins/connection/redirection_utility.c b/plugins/connection/redirection_utility.c new file mode 100644 index 000000000..7402101a7 --- /dev/null +++ b/plugins/connection/redirection_utility.c @@ -0,0 +1,555 @@ +/************************************************************************************ + Copyright (C) 2015 MariaDB Corporation AB, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA +*************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ma_server_error.h" +#include +#include "redirection_utility.h" + +// Note that key, host and user and malloced together with Redirection_Entry +struct Redirection_Entry +{ + char* key; + char* host; + char* user; + unsigned int port; + struct Redirection_Entry* next; + struct Redirection_Entry* prev; +}; + +// Note that host and user are malloced together with Redirection_info +struct Redirection_Info +{ + char* host; + char* user; + unsigned int port; +}; + +#define MAX_CACHED_REDIRECTION_ENTRYS 4 +#define MAX_REDIRECTION_KEY_LENGTH 512 +#define MAX_REDIRECTION_INFO_LENGTH 512 +static unsigned int redirection_cache_num = 0; +static struct Redirection_Entry redirection_cache_root = { 0 }; +static struct Redirection_Entry* redirection_cache_rear = NULL; + +static pthread_mutex_t redirect_lock; + +#define redirect_mutex redirect_lock + +#define mutex_lock(x) pthread_mutex_lock(&x); + +#define mutex_unlock(x) pthread_mutex_unlock(&x) + +#define mutex_destroy(x) pthread_mutex_destroy(&x) + +int init_redirection_cache() +{ + pthread_mutex_init(&redirect_mutex, NULL); + + redirection_cache_root.next = NULL; + redirection_cache_rear = &redirection_cache_root; + redirection_cache_num = 0; + + return 0; +} + +void add_redirection_entry(char* key, char* host, char* user, unsigned int port); + +void delete_redirection_entry(char* key); + +my_bool get_redirection_info(char* key, struct Redirection_Info** info, my_bool copy_on_found); + +struct Redirection_Info* parse_redirection_info(MYSQL* mysql); + +MYSQL* check_redirect(MYSQL* mysql, const char* host, + const char* user, const char* passwd, + const char* db, uint port, + const char* unix_socket, + ulong client_flag) +{ + if (mysql->options.redirection_mode != REDIRECTION_OFF) + { + const char redirect_key[MAX_REDIRECTION_KEY_LENGTH] = { 0 }; + struct Redirection_Info* info = NULL; + + if (!port) + port = MARIADB_PORT; + + sprintf(redirect_key, "%s_%s_%u", host, user, port); + + my_bool copy_on_found = 1; + if (get_redirection_info(redirect_key, &info, copy_on_found)) + { + MYSQL* ret_mysql = mysql->methods->api->mysql_real_connect(mysql, + info->host, + info->user, + passwd, + db, + info->port, + unix_socket, + client_flag | CLIENT_REMEMBER_OPTIONS); + + free(info); + + if (ret_mysql) + { + return ret_mysql; + } + else + { + // redirection_entry is incorrect or has expired, delete entry before going back to normal workflow + delete_redirection_entry(redirect_key); + } + } + } + + return NULL; +} + +MYSQL* redirect(MYSQL* mysql, const char* host, + const char* user, const char* passwd, + const char* db, uint port, + const char* unix_socket, + ulong client_flag) +{ + if (mysql->options.redirection_mode == REDIRECTION_OFF) + return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); + + // create a temp connection to gateway to retrieve redirection info + MYSQL tmp_mysql; + my_bool use_ssl = 1; + if (mysql->options.use_ssl == 0) + use_ssl = 0; + mysql->methods->api->mysql_init(&tmp_mysql); + mysql->methods->api->mysql_options(&tmp_mysql, MYSQL_OPT_SSL_ENFORCE, &use_ssl); + MYSQL* ret_mysql = mysql->methods->db_connect(&tmp_mysql, host, user, passwd, + db, port, unix_socket, client_flag); + + if (!ret_mysql) { + mysql->methods->set_error(mysql, tmp_mysql.net.last_errno, + tmp_mysql.net.sqlstate, + tmp_mysql.net.last_error); + mysql->methods->api->mysql_close(&tmp_mysql); + return NULL; + } + + // redirection info is present in tmp_mysql.info if redirection enabled on both ends + struct Redirection_Info* info = NULL; + info = parse_redirection_info(&tmp_mysql); + mysql->methods->api->mysql_close(&tmp_mysql); + + if (!info) + { + if (mysql->options.redirection_mode == REDIRECTION_ON) + { + mysql->methods->set_error(mysql, ER_PARSE_ERROR, "HY000", + "redirection set to ON on client side but parse_redirection_info failed. Redirection info: %s", + mysql->info); + return NULL; + } + else + { + // enable_redirect = PREFERRED, fallback to normal connection workflow + return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); + } + } + + // No need to redirect if we are talking directly to the server + if (!strcmp(info->host, host) && + !strcmp(info->user, user) && + info->port == port) + { + free(info); + return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); + } + + // the "real" connection + if (!mysql->methods->db_connect(mysql, info->host, info->user, passwd, + db, info->port, unix_socket, client_flag)) + { + free(info); + if (mysql->options.redirection_mode == REDIRECTION_ON) + { + mysql->methods->set_error(mysql, ER_BAD_HOST_ERROR, "HY000", + "redirection set to ON on client side but parse_redirection_info failed. Redirection info: %s", + mysql->info); + return NULL; + } + else + { + // enable_redirect = PREFERRED, fallback to normal connection workflow + return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); + } + return NULL; + } + + // real_connect succeeded + free(info); + + const char redirect_key[MAX_REDIRECTION_KEY_LENGTH] = { 0 }; + sprintf(redirect_key, "%s_%s_%u", host, user, port); + add_redirection_entry(redirect_key, mysql->host, mysql->user, mysql->port); + + return mysql; +} + +struct Redirection_Info* parse_redirection_info(MYSQL* mysql) +{ + struct Redirection_Info* info = NULL; + if (!mysql->info || !mysql->info[0]) + { + return NULL; + } + + /* + redirection info format: + Location: mysql://[redirectedHostName]:redirectedPort/?user=redirectedUser&ttl=%d\n + */ + const char* msg_header = "Location: mysql://["; + int msg_header_len = strlen(msg_header); + + char* info_str = strstr(mysql->info, msg_header); + char* cur_pos = info_str + msg_header_len; + char* end = info_str + strlen(info_str); + + char* host_begin = cur_pos, * host_end = NULL, + * port_begin = NULL, * port_end = NULL, + * user_begin = NULL, * user_end = NULL, + * ttl_begin = NULL, * ttl_end = NULL; + + host_end = strchr(cur_pos, ']'); + if (host_end == NULL) return FALSE; + + cur_pos = host_end + 1; + if (cur_pos == end || *cur_pos != ':' || ++cur_pos == end) return NULL; + + port_begin = cur_pos; + port_end = strchr(cur_pos, '/'); + if (port_end == NULL) return NULL; + + cur_pos = port_end + 1; + if (cur_pos == end || *cur_pos != '?' || ++cur_pos == end) return NULL; + + int user_delimiter_len = strlen("user="); + if (end - cur_pos <= user_delimiter_len || strncmp(cur_pos, "user=", user_delimiter_len) != 0) return NULL; + + user_begin = cur_pos + user_delimiter_len; + user_end = strchr(cur_pos, '&'); + if (user_end == NULL) return NULL; + + cur_pos = user_end + 1; + if (cur_pos == end) return NULL; + + int ttl_delimiter_len = strlen("ttl="); + if (end - cur_pos <= ttl_delimiter_len || strncmp(cur_pos, "ttl=", ttl_delimiter_len) != 0) return NULL; + + ttl_begin = cur_pos + ttl_delimiter_len; + ttl_end = strchr(cur_pos, '\n'); + if (ttl_end == NULL) return NULL; + + int host_len = host_end - host_begin; + int port_len = port_end - port_begin; + int user_len = user_end - user_begin; + int ttl_len = ttl_end - ttl_begin; + int redirection_info_length = ttl_end - info_str; + + // server side protocol rules that redirection_info_length should not exceed 512 bytes + if (host_len <= 0 || port_len <= 0 || user_len <= 0 || ttl_len <= 0 || redirection_info_length > MAX_REDIRECTION_INFO_LENGTH) { + return NULL; + } + + char* host_str = NULL; + char* user_str = NULL; + + // free(info) will free all pointers alloced by this ma_multi_malloc + // do not include port_str here because port_str is a temp var, whereas port is the real member + // of redirection info, so port_str should not get out of this function's scope + if (!ma_multi_malloc(0, + &info, sizeof(struct Redirection_Info), + &host_str, (size_t)host_len + 1, + &user_str, (size_t)user_len + 1, + NULL)) + return NULL; + + char* port_str = (char*)malloc((size_t)port_len + 1); + + ma_strmake(host_str, host_begin, host_len); + ma_strmake(user_str, user_begin, user_len); + ma_strmake(port_str, port_begin, port_len); + info->host = host_str; + info->user = user_str; + + if (!(info->port = strtoul(port_str, NULL, 0))) + { + free(info); + free(port_str); + return NULL; + } + + free(port_str); + return info; +} + +my_bool get_redirection_info(char* key, struct Redirection_Info** info, my_bool copy_on_found) +{ + struct Redirection_Entry* ret_entry = NULL; + struct Redirection_Entry* cur_entry = NULL; + + mutex_lock(redirect_mutex); + cur_entry = redirection_cache_root.next; + while (cur_entry) + { + if (cur_entry->key) + { + if (!strcmp(cur_entry->key, key)) + { + ret_entry = cur_entry; + break; + } + cur_entry = cur_entry->next; + } + else break; + } + + if (ret_entry) + { + // after find the cache, move it to list header + if (redirection_cache_root.next != ret_entry) + { + if (redirection_cache_rear == ret_entry) + { + redirection_cache_rear = ret_entry->prev; + } + + ret_entry->prev->next = ret_entry->next; + + if (ret_entry->next) + { + ret_entry->next->prev = ret_entry->prev; + } + + ret_entry->next = redirection_cache_root.next; + ret_entry->prev = &redirection_cache_root; + + redirection_cache_root.next = ret_entry; + } + + if (copy_on_found) + { + size_t host_len = strlen(ret_entry->host); + size_t user_len = strlen(ret_entry->user); + + char* host_str; + char* user_str; + + if (!ma_multi_malloc(0, + info, sizeof(struct Redirection_Info), + &host_str, host_len + 1, + &user_str, user_len + 1, + NULL)) + { + mutex_unlock(redirect_mutex); + return 0; + } + + ma_strmake(host_str, ret_entry->host, strlen(ret_entry->host)); + ma_strmake(user_str, ret_entry->user, strlen(ret_entry->user)); + (*info)->host = host_str; + (*info)->user = user_str; + (*info)->port = ret_entry->port; + } + } + + my_bool retval = (ret_entry != NULL); + mutex_unlock(redirect_mutex); + + return retval; +} + +void add_redirection_entry(char* key, char* host, char* user, unsigned int port) +{ + struct Redirection_Entry* new_entry = NULL; + struct Redirection_Entry* popingEntry = NULL; + char* _key; + char* _host; + char* _user; + size_t key_len = 0; + size_t host_len = 0; + size_t user_len = 0; + + if (key == NULL || host == NULL || user == NULL) + { + return; + } + + my_bool copy_on_found = 0; + if (get_redirection_info(key, NULL, copy_on_found)) + { + return; + } + + key_len = strlen(key); + host_len = strlen(host); + user_len = strlen(user); + + if (!ma_multi_malloc(0, + &new_entry, sizeof(struct Redirection_Entry), + &_key, key_len + 1, + &_host, host_len + 1, + &_user, user_len + 1, + NULL)) + { + return; + } + + new_entry->key = _key; + new_entry->host = _host; + new_entry->user = _user; + new_entry->next = NULL; + new_entry->prev = NULL; + + strcpy(new_entry->key, key); + strcpy(new_entry->host, host); + strcpy(new_entry->user, user); + new_entry->port = port; + + mutex_lock(redirect_mutex); + + if (NULL == redirection_cache_root.next) + { + redirection_cache_root.next = new_entry; + new_entry->prev = &redirection_cache_root; + redirection_cache_rear = new_entry; + } + else + { + new_entry->next = redirection_cache_root.next; + new_entry->prev = &redirection_cache_root; + + redirection_cache_root.next->prev = new_entry; + redirection_cache_root.next = new_entry; + } + + redirection_cache_num++; + + if (redirection_cache_num > MAX_CACHED_REDIRECTION_ENTRYS) + { + popingEntry = redirection_cache_rear; + redirection_cache_rear = redirection_cache_rear->prev; + redirection_cache_rear->next = NULL; + + redirection_cache_num--; + } + + mutex_unlock(redirect_mutex); + + if (popingEntry) + free(popingEntry); + + return; +} + +void delete_redirection_entry(char* key) +{ + struct Redirection_Entry* del_entry = NULL; + struct Redirection_Entry* cur_entry = NULL; + + mutex_lock(redirect_mutex); + cur_entry = redirection_cache_root.next; + while (cur_entry) + { + if (cur_entry->key) + { + if (!strcmp(cur_entry->key, key)) + { + del_entry = cur_entry; + break; + } + cur_entry = cur_entry->next; + } + else break; + } + + if (del_entry) + { + if (redirection_cache_rear == del_entry) + { + redirection_cache_rear = del_entry->prev; + } + del_entry->prev->next = del_entry->next; + + if (del_entry->next) + { + del_entry->next->prev = del_entry->prev; + } + + redirection_cache_num--; + } + + mutex_unlock(redirect_mutex); + + if (del_entry) + free(del_entry); +} + +void* ma_multi_malloc(myf myFlags, ...) +{ + va_list args; + char** ptr, * start, * res; + size_t tot_length, length; + + va_start(args, myFlags); + tot_length = 0; + while ((ptr = va_arg(args, char**))) + { + length = va_arg(args, size_t); + tot_length += ALIGN_SIZE(length); + } + va_end(args); + + if (!(start = (char*)malloc(tot_length))) + return 0; + + va_start(args, myFlags); + res = start; + while ((ptr = va_arg(args, char**))) + { + *ptr = res; + length = va_arg(args, size_t); + res += ALIGN_SIZE(length); + } + va_end(args); + return start; +} + +char* ma_strmake(register char* dst, register const char* src, size_t length) +{ + while (length--) + if (!(*dst++ = *src++)) + return dst - 1; + *dst = 0; + return dst; +} \ No newline at end of file diff --git a/plugins/connection/redirection_utility.h b/plugins/connection/redirection_utility.h new file mode 100644 index 000000000..c8b5016a7 --- /dev/null +++ b/plugins/connection/redirection_utility.h @@ -0,0 +1,18 @@ +#ifndef MA_REDIRECT_H +#define MA_REDIRECT_H + +#include + +MYSQL* redirect(MYSQL* mysql, const char* host, + const char* user, const char* passwd, + const char* db, uint port, + const char* unix_socket, + ulong client_flag); +MYSQL* check_redirect(MYSQL* mysql, const char* host, + const char* user, const char* passwd, + const char* db, unsigned int port, + const char* unix_socket, + unsigned long client_flag); +int init_redirection_cache(); + +#endif \ No newline at end of file From 3d336156688fa4b937a9c7c48379dab39b21354a Mon Sep 17 00:00:00 2001 From: Shihao Chen Date: Mon, 1 Jun 2020 13:32:48 +0800 Subject: [PATCH 2/4] format fix --- plugins/connection/redirection_utility.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/connection/redirection_utility.c b/plugins/connection/redirection_utility.c index 7402101a7..749014c67 100644 --- a/plugins/connection/redirection_utility.c +++ b/plugins/connection/redirection_utility.c @@ -241,7 +241,7 @@ struct Redirection_Info* parse_redirection_info(MYSQL* mysql) * ttl_begin = NULL, * ttl_end = NULL; host_end = strchr(cur_pos, ']'); - if (host_end == NULL) return FALSE; + if (host_end == NULL) return NULL; cur_pos = host_end + 1; if (cur_pos == end || *cur_pos != ':' || ++cur_pos == end) return NULL; From f43df3d62f070a573e22bfa85c42ea142104e16a Mon Sep 17 00:00:00 2001 From: Shihao Chen Date: Wed, 3 Jun 2020 14:54:19 +0800 Subject: [PATCH 3/4] fix according to comments --- include/ma_common.h | 1 + include/mysql.h | 7 ++- libmariadb/mariadb_lib.c | 7 ++- plugins/connection/redirection.c | 4 +- plugins/connection/redirection_utility.c | 56 ++++++++++++++---------- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/include/ma_common.h b/include/ma_common.h index 123a5104a..3b0201e4d 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -79,6 +79,7 @@ struct st_mysql_options_extension { char *proxy_header; size_t proxy_header_len; int (*io_wait)(my_socket handle, my_bool is_read, int timeout); + enable_redirect redirection_mode; }; typedef struct st_connection_handler diff --git a/include/mysql.h b/include/mysql.h index d6b59ae65..562aef625 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -216,7 +216,6 @@ extern const char *SQLSTATE_UNKNOWN; MYSQL_OPT_MAX_ALLOWED_PACKET, MYSQL_OPT_NET_BUFFER_LENGTH, MYSQL_OPT_TLS_VERSION, - MYSQL_OPT_USE_REDIRECTION, /* MariaDB specific */ MYSQL_PROGRESS_CALLBACK=5999, @@ -246,7 +245,8 @@ extern const char *SQLSTATE_UNKNOWN; MARIADB_OPT_MULTI_STATEMENTS, MARIADB_OPT_INTERACTIVE, MARIADB_OPT_PROXY_HEADER, - MARIADB_OPT_IO_WAIT + MARIADB_OPT_IO_WAIT, + MARIADB_OPT_USE_REDIRECTION }; enum mariadb_value { @@ -303,7 +303,7 @@ extern const char *SQLSTATE_UNKNOWN; MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY }; - typedef enum mysql_redirection_mode + typedef enum mariadb_redirection_mode { REDIRECTION_OFF, REDIRECTION_ON, @@ -331,7 +331,6 @@ struct st_mysql_options { char *bind_address; my_bool secure_auth; my_bool report_data_truncation; - enable_redirect redirection_mode; /* function pointers for local infile support */ int (*local_infile_init)(void **, const char *, void *); int (*local_infile_read)(void *, char *, unsigned int); diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index ed9911abb..4e6dc7793 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -3053,8 +3053,8 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) mysql->options.extension->connect_attrs_len= 0; } break; - case MYSQL_OPT_USE_REDIRECTION: - mysql->options.redirection_mode = *(enable_redirect*)arg1; + case MARIADB_OPT_USE_REDIRECTION: + OPT_SET_EXTENDED_VALUE(&mysql->options, redirection_mode, *(enable_redirect*)arg1); break; case MARIADB_OPT_CONNECTION_HANDLER: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, connection_handler, (char *)arg1); @@ -3419,6 +3419,9 @@ mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...) case MYSQL_OPT_BIND: *((char **)arg)= mysql->options.bind_address; break; + case MARIADB_OPT_USE_REDIRECTION: + *((enable_redirect *)arg) = mysql->options.extension ? mysql->options.extension->redirection_mode : REDIRECTION_OFF; + break; case MARIADB_OPT_TLS_CIPHER_STRENGTH: *((unsigned int *)arg) = mysql->options.extension ? mysql->options.extension->tls_cipher_strength : 0; break; diff --git a/plugins/connection/redirection.c b/plugins/connection/redirection.c index d418efba5..d5e8c5a90 100644 --- a/plugins/connection/redirection.c +++ b/plugins/connection/redirection.c @@ -1,5 +1,5 @@ /************************************************************************************ - Copyright (C) 2015-2018 MariaDB Corporation AB + Copyright (C) 2020 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -15,8 +15,6 @@ License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA - Part of this code includes code from the PHP project which - is freely available from http://www.php.net *************************************************************************************/ /* MariaDB Connection plugin for redirection. */ #include diff --git a/plugins/connection/redirection_utility.c b/plugins/connection/redirection_utility.c index 749014c67..28121bd1d 100644 --- a/plugins/connection/redirection_utility.c +++ b/plugins/connection/redirection_utility.c @@ -1,5 +1,5 @@ /************************************************************************************ - Copyright (C) 2015 MariaDB Corporation AB, + Copyright (C) 2020 MariaDB Corporation AB, This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -29,7 +29,7 @@ #include #include "redirection_utility.h" -// Note that key, host and user and malloced together with Redirection_Entry +/* Note that key, host and user and malloced together with Redirection_Entry */ struct Redirection_Entry { char* key; @@ -40,7 +40,7 @@ struct Redirection_Entry struct Redirection_Entry* prev; }; -// Note that host and user are malloced together with Redirection_info +/* Note that host and user are malloced together with Redirection_info */ struct Redirection_Info { char* host; @@ -90,7 +90,10 @@ MYSQL* check_redirect(MYSQL* mysql, const char* host, const char* unix_socket, ulong client_flag) { - if (mysql->options.redirection_mode != REDIRECTION_OFF) + enable_redirect redirection_mode; + mysql->methods->api->mysql_get_option(mysql, MARIADB_OPT_USE_REDIRECTION, &redirection_mode); + + if (redirection_mode != REDIRECTION_OFF) { const char redirect_key[MAX_REDIRECTION_KEY_LENGTH] = { 0 }; struct Redirection_Info* info = NULL; @@ -98,7 +101,7 @@ MYSQL* check_redirect(MYSQL* mysql, const char* host, if (!port) port = MARIADB_PORT; - sprintf(redirect_key, "%s_%s_%u", host, user, port); + snprintf(redirect_key, MAX_REDIRECTION_KEY_LENGTH, "%s_%s_%u", host, user, port); my_bool copy_on_found = 1; if (get_redirection_info(redirect_key, &info, copy_on_found)) @@ -120,7 +123,7 @@ MYSQL* check_redirect(MYSQL* mysql, const char* host, } else { - // redirection_entry is incorrect or has expired, delete entry before going back to normal workflow + /* redirection_entry is incorrect or has expired, delete entry before going back to normal workflow */ delete_redirection_entry(redirect_key); } } @@ -135,10 +138,13 @@ MYSQL* redirect(MYSQL* mysql, const char* host, const char* unix_socket, ulong client_flag) { - if (mysql->options.redirection_mode == REDIRECTION_OFF) + enable_redirect redirection_mode; + mysql->methods->api->mysql_get_option(mysql, MARIADB_OPT_USE_REDIRECTION, &redirection_mode); + + if (redirection_mode == REDIRECTION_OFF) return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); - // create a temp connection to gateway to retrieve redirection info + /* create a temp connection to gateway to retrieve redirection info */ MYSQL tmp_mysql; my_bool use_ssl = 1; if (mysql->options.use_ssl == 0) @@ -156,14 +162,14 @@ MYSQL* redirect(MYSQL* mysql, const char* host, return NULL; } - // redirection info is present in tmp_mysql.info if redirection enabled on both ends + /* redirection info is present in tmp_mysql.info if redirection enabled on both ends */ struct Redirection_Info* info = NULL; info = parse_redirection_info(&tmp_mysql); mysql->methods->api->mysql_close(&tmp_mysql); if (!info) { - if (mysql->options.redirection_mode == REDIRECTION_ON) + if (redirection_mode == REDIRECTION_ON) { mysql->methods->set_error(mysql, ER_PARSE_ERROR, "HY000", "redirection set to ON on client side but parse_redirection_info failed. Redirection info: %s", @@ -172,12 +178,12 @@ MYSQL* redirect(MYSQL* mysql, const char* host, } else { - // enable_redirect = PREFERRED, fallback to normal connection workflow + /* enable_redirect = PREFERRED, fallback to normal connection workflow */ return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); } } - // No need to redirect if we are talking directly to the server + /* No need to redirect if we are talking directly to the server */ if (!strcmp(info->host, host) && !strcmp(info->user, user) && info->port == port) @@ -186,12 +192,12 @@ MYSQL* redirect(MYSQL* mysql, const char* host, return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); } - // the "real" connection + /* the "real" connection */ if (!mysql->methods->db_connect(mysql, info->host, info->user, passwd, db, info->port, unix_socket, client_flag)) { free(info); - if (mysql->options.redirection_mode == REDIRECTION_ON) + if (redirection_mode == REDIRECTION_ON) { mysql->methods->set_error(mysql, ER_BAD_HOST_ERROR, "HY000", "redirection set to ON on client side but parse_redirection_info failed. Redirection info: %s", @@ -200,17 +206,17 @@ MYSQL* redirect(MYSQL* mysql, const char* host, } else { - // enable_redirect = PREFERRED, fallback to normal connection workflow + /* enable_redirect = PREFERRED, fallback to normal connection workflow */ return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); } return NULL; } - // real_connect succeeded + /* real_connect succeeded */ free(info); const char redirect_key[MAX_REDIRECTION_KEY_LENGTH] = { 0 }; - sprintf(redirect_key, "%s_%s_%u", host, user, port); + snprintf(redirect_key, MAX_REDIRECTION_KEY_LENGTH, "%s_%s_%u", host, user, port); add_redirection_entry(redirect_key, mysql->host, mysql->user, mysql->port); return mysql; @@ -276,7 +282,7 @@ struct Redirection_Info* parse_redirection_info(MYSQL* mysql) int ttl_len = ttl_end - ttl_begin; int redirection_info_length = ttl_end - info_str; - // server side protocol rules that redirection_info_length should not exceed 512 bytes + /* server side protocol rules that redirection_info_length should not exceed 512 bytes */ if (host_len <= 0 || port_len <= 0 || user_len <= 0 || ttl_len <= 0 || redirection_info_length > MAX_REDIRECTION_INFO_LENGTH) { return NULL; } @@ -284,9 +290,11 @@ struct Redirection_Info* parse_redirection_info(MYSQL* mysql) char* host_str = NULL; char* user_str = NULL; - // free(info) will free all pointers alloced by this ma_multi_malloc - // do not include port_str here because port_str is a temp var, whereas port is the real member - // of redirection info, so port_str should not get out of this function's scope + /* + free(info) will free all pointers alloced by this ma_multi_malloc + do not include port_str here because port_str is a temp var, whereas port is the real member + of redirection info, so port_str should not get out of this function's scope + */ if (!ma_multi_malloc(0, &info, sizeof(struct Redirection_Info), &host_str, (size_t)host_len + 1, @@ -336,7 +344,7 @@ my_bool get_redirection_info(char* key, struct Redirection_Info** info, my_bool if (ret_entry) { - // after find the cache, move it to list header + /* after find the cache, move it to list header */ if (redirection_cache_root.next != ret_entry) { if (redirection_cache_rear == ret_entry) @@ -515,6 +523,7 @@ void delete_redirection_entry(char* key) free(del_entry); } +#ifdef PLUGIN_DYNAMIC void* ma_multi_malloc(myf myFlags, ...) { va_list args; @@ -552,4 +561,5 @@ char* ma_strmake(register char* dst, register const char* src, size_t length) return dst - 1; *dst = 0; return dst; -} \ No newline at end of file +} +#endif \ No newline at end of file From 15c1d1f7f5edbf614b951e6565785055bf2ebc30 Mon Sep 17 00:00:00 2001 From: Shihao Chen Date: Tue, 9 Jun 2020 17:36:19 +0800 Subject: [PATCH 4/4] rewrite redirection cache as an array(not a list) of cache entries --- plugins/connection/redirection_utility.c | 329 +++++++++-------------- 1 file changed, 131 insertions(+), 198 deletions(-) diff --git a/plugins/connection/redirection_utility.c b/plugins/connection/redirection_utility.c index 28121bd1d..4c07033fc 100644 --- a/plugins/connection/redirection_utility.c +++ b/plugins/connection/redirection_utility.c @@ -28,6 +28,7 @@ #include "ma_server_error.h" #include #include "redirection_utility.h" +#include /* Note that key, host and user and malloced together with Redirection_Entry */ struct Redirection_Entry @@ -36,8 +37,7 @@ struct Redirection_Entry char* host; char* user; unsigned int port; - struct Redirection_Entry* next; - struct Redirection_Entry* prev; + time_t last_used; }; /* Note that host and user are malloced together with Redirection_info */ @@ -48,12 +48,11 @@ struct Redirection_Info unsigned int port; }; -#define MAX_CACHED_REDIRECTION_ENTRYS 4 +#define MAX_CACHED_REDIRECTION_ENTRIES 4 #define MAX_REDIRECTION_KEY_LENGTH 512 #define MAX_REDIRECTION_INFO_LENGTH 512 -static unsigned int redirection_cache_num = 0; -static struct Redirection_Entry redirection_cache_root = { 0 }; -static struct Redirection_Entry* redirection_cache_rear = NULL; + +static struct Redirection_Entry** redirection_cache = NULL; static pthread_mutex_t redirect_lock; @@ -65,25 +64,30 @@ static pthread_mutex_t redirect_lock; #define mutex_destroy(x) pthread_mutex_destroy(&x) -int init_redirection_cache() -{ - pthread_mutex_init(&redirect_mutex, NULL); - - redirection_cache_root.next = NULL; - redirection_cache_rear = &redirection_cache_root; - redirection_cache_num = 0; - - return 0; -} +my_bool check_redirection_entry(char* key); void add_redirection_entry(char* key, char* host, char* user, unsigned int port); void delete_redirection_entry(char* key); -my_bool get_redirection_info(char* key, struct Redirection_Info** info, my_bool copy_on_found); +struct Redirection_Entry* allocate_redirection_entry(char* key, char* host, char* user, unsigned int port); + +struct Redirection_Info* get_redirection_info(char* key); + +struct Redirection_Info* allocate_redirection_info(char* host, size_t host_len, char* user, size_t user_len, unsigned int port); struct Redirection_Info* parse_redirection_info(MYSQL* mysql); +int init_redirection_cache() +{ + pthread_mutex_init(&redirect_mutex, NULL); + + redirection_cache = (struct Redirection_Entry**)malloc(sizeof(struct Redirection_Entry*) * MAX_CACHED_REDIRECTION_ENTRIES); + memset(redirection_cache, 0, sizeof(struct Redirection_Entry*) * MAX_CACHED_REDIRECTION_ENTRIES); + + return 0; +} + MYSQL* check_redirect(MYSQL* mysql, const char* host, const char* user, const char* passwd, const char* db, uint port, @@ -96,15 +100,14 @@ MYSQL* check_redirect(MYSQL* mysql, const char* host, if (redirection_mode != REDIRECTION_OFF) { const char redirect_key[MAX_REDIRECTION_KEY_LENGTH] = { 0 }; - struct Redirection_Info* info = NULL; if (!port) port = MARIADB_PORT; snprintf(redirect_key, MAX_REDIRECTION_KEY_LENGTH, "%s_%s_%u", host, user, port); - my_bool copy_on_found = 1; - if (get_redirection_info(redirect_key, &info, copy_on_found)) + struct Redirection_Info* info = get_redirection_info(redirect_key); + if (info) { MYSQL* ret_mysql = mysql->methods->api->mysql_real_connect(mysql, info->host, @@ -140,7 +143,7 @@ MYSQL* redirect(MYSQL* mysql, const char* host, { enable_redirect redirection_mode; mysql->methods->api->mysql_get_option(mysql, MARIADB_OPT_USE_REDIRECTION, &redirection_mode); - + if (redirection_mode == REDIRECTION_OFF) return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); @@ -224,12 +227,11 @@ MYSQL* redirect(MYSQL* mysql, const char* host, struct Redirection_Info* parse_redirection_info(MYSQL* mysql) { - struct Redirection_Info* info = NULL; if (!mysql->info || !mysql->info[0]) { return NULL; } - + /* redirection info format: Location: mysql://[redirectedHostName]:redirectedPort/?user=redirectedUser&ttl=%d\n @@ -287,240 +289,171 @@ struct Redirection_Info* parse_redirection_info(MYSQL* mysql) return NULL; } - char* host_str = NULL; - char* user_str = NULL; - - /* - free(info) will free all pointers alloced by this ma_multi_malloc - do not include port_str here because port_str is a temp var, whereas port is the real member - of redirection info, so port_str should not get out of this function's scope - */ - if (!ma_multi_malloc(0, - &info, sizeof(struct Redirection_Info), - &host_str, (size_t)host_len + 1, - &user_str, (size_t)user_len + 1, - NULL)) - return NULL; - + // convert port to integer before allocating redirection info char* port_str = (char*)malloc((size_t)port_len + 1); - - ma_strmake(host_str, host_begin, host_len); - ma_strmake(user_str, user_begin, user_len); ma_strmake(port_str, port_begin, port_len); - info->host = host_str; - info->user = user_str; - - if (!(info->port = strtoul(port_str, NULL, 0))) - { - free(info); - free(port_str); + unsigned int port = strtoul(port_str, NULL, 0); + free(port_str); + if (!port) return NULL; - } - free(port_str); + struct Redirection_Info* info = allocate_redirection_info(host_begin, host_len, user_begin, user_len, port); return info; } -my_bool get_redirection_info(char* key, struct Redirection_Info** info, my_bool copy_on_found) -{ +struct Redirection_Info* get_redirection_info(char* key) { struct Redirection_Entry* ret_entry = NULL; - struct Redirection_Entry* cur_entry = NULL; - mutex_lock(redirect_mutex); - cur_entry = redirection_cache_root.next; - while (cur_entry) - { - if (cur_entry->key) - { - if (!strcmp(cur_entry->key, key)) - { - ret_entry = cur_entry; - break; - } - cur_entry = cur_entry->next; - } - else break; + for (int i = 0; i < MAX_CACHED_REDIRECTION_ENTRIES; ++i) { + if (redirection_cache[i] && redirection_cache[i]->key && !strcmp(redirection_cache[i]->key, key)) + redirection_cache[i]->last_used = time(NULL); + ret_entry = redirection_cache[i]; + break; } - if (ret_entry) - { - /* after find the cache, move it to list header */ - if (redirection_cache_root.next != ret_entry) - { - if (redirection_cache_rear == ret_entry) - { - redirection_cache_rear = ret_entry->prev; - } + if (!ret_entry) { + mutex_unlock(redirect_mutex); + return NULL; + } - ret_entry->prev->next = ret_entry->next; + struct Redirection_Info* info = allocate_redirection_info(ret_entry->host, strlen(ret_entry->host), + ret_entry->user, strlen(ret_entry->user), ret_entry->port); + mutex_unlock(redirect_mutex); + return info; +} - if (ret_entry->next) - { - ret_entry->next->prev = ret_entry->prev; - } +struct Redirection_Info* allocate_redirection_info(char* host, size_t host_len, char* user, size_t user_len, unsigned int port) { + struct Redirection_Info* info = NULL; - ret_entry->next = redirection_cache_root.next; - ret_entry->prev = &redirection_cache_root; + char* host_str; + char* user_str; - redirection_cache_root.next = ret_entry; - } + if (!ma_multi_malloc(0, + &info, sizeof(struct Redirection_Info), + &host_str, host_len + 1, + &user_str, user_len + 1, + NULL)) + { + return NULL; + } - if (copy_on_found) - { - size_t host_len = strlen(ret_entry->host); - size_t user_len = strlen(ret_entry->user); + ma_strmake(host_str, host, host_len); + ma_strmake(user_str, user, user_len); + (info)->host = host_str; + (info)->user = user_str; + (info)->port = port; - char* host_str; - char* user_str; + return info; +} - if (!ma_multi_malloc(0, - info, sizeof(struct Redirection_Info), - &host_str, host_len + 1, - &user_str, user_len + 1, - NULL)) - { - mutex_unlock(redirect_mutex); - return 0; - } +my_bool check_redirection_entry(char* key) { + my_bool found = 0; + mutex_lock(redirect_mutex); - ma_strmake(host_str, ret_entry->host, strlen(ret_entry->host)); - ma_strmake(user_str, ret_entry->user, strlen(ret_entry->user)); - (*info)->host = host_str; - (*info)->user = user_str; - (*info)->port = ret_entry->port; + for (int i = 0; i < MAX_CACHED_REDIRECTION_ENTRIES; ++i) { + if (redirection_cache[i] && redirection_cache[i]->key && !strcmp(redirection_cache[i]->key, key)) { + redirection_cache[i]->last_used = time(NULL); + found = 1; + break; } } - my_bool retval = (ret_entry != NULL); mutex_unlock(redirect_mutex); - - return retval; + return found; } -void add_redirection_entry(char* key, char* host, char* user, unsigned int port) +void add_redirection_entry(char* key, char* host, char* user, unsigned int port) { - struct Redirection_Entry* new_entry = NULL; - struct Redirection_Entry* popingEntry = NULL; - char* _key; - char* _host; - char* _user; - size_t key_len = 0; - size_t host_len = 0; - size_t user_len = 0; if (key == NULL || host == NULL || user == NULL) { return; } - my_bool copy_on_found = 0; - if (get_redirection_info(key, NULL, copy_on_found)) + if (check_redirection_entry(key)) { return; } - key_len = strlen(key); - host_len = strlen(host); - user_len = strlen(user); - - if (!ma_multi_malloc(0, - &new_entry, sizeof(struct Redirection_Entry), - &_key, key_len + 1, - &_host, host_len + 1, - &_user, user_len + 1, - NULL)) - { + struct Redirection_Entry* new_entry = allocate_redirection_entry(key, host, user, port); + if (!new_entry) { return; } - new_entry->key = _key; - new_entry->host = _host; - new_entry->user = _user; - new_entry->next = NULL; - new_entry->prev = NULL; - - strcpy(new_entry->key, key); - strcpy(new_entry->host, host); - strcpy(new_entry->user, user); - new_entry->port = port; - + time_t least_recent_use_time = new_entry->last_used; + int poping_index = -1; + struct Redirection_Entry* poping_entry = NULL; mutex_lock(redirect_mutex); + for (int i = 0; i < MAX_CACHED_REDIRECTION_ENTRIES; ++i) { + if (redirection_cache[i] == NULL) { + redirection_cache[i] = new_entry; + mutex_unlock(redirect_mutex); + return; + } - if (NULL == redirection_cache_root.next) - { - redirection_cache_root.next = new_entry; - new_entry->prev = &redirection_cache_root; - redirection_cache_rear = new_entry; - } - else - { - new_entry->next = redirection_cache_root.next; - new_entry->prev = &redirection_cache_root; - - redirection_cache_root.next->prev = new_entry; - redirection_cache_root.next = new_entry; + if (redirection_cache[i]->last_used < least_recent_use_time) { + poping_index = i; + least_recent_use_time = redirection_cache[i]->last_used; + } } - redirection_cache_num++; - - if (redirection_cache_num > MAX_CACHED_REDIRECTION_ENTRYS) - { - popingEntry = redirection_cache_rear; - redirection_cache_rear = redirection_cache_rear->prev; - redirection_cache_rear->next = NULL; - - redirection_cache_num--; + if (poping_index != -1) { + poping_entry = redirection_cache[poping_index]; + redirection_cache[poping_index] = new_entry; } mutex_unlock(redirect_mutex); - - if (popingEntry) - free(popingEntry); - - return; + if (poping_entry) { + free(poping_entry); + } } -void delete_redirection_entry(char* key) -{ +void delete_redirection_entry(char* key) { struct Redirection_Entry* del_entry = NULL; - struct Redirection_Entry* cur_entry = NULL; - mutex_lock(redirect_mutex); - cur_entry = redirection_cache_root.next; - while (cur_entry) - { - if (cur_entry->key) - { - if (!strcmp(cur_entry->key, key)) - { - del_entry = cur_entry; - break; - } - cur_entry = cur_entry->next; + + for (int i = 0; i < MAX_CACHED_REDIRECTION_ENTRIES; ++i) { + if (redirection_cache[i] && redirection_cache[i]->key && !strcmp(redirection_cache[i]->key, key)) { + del_entry = redirection_cache[i]; + redirection_cache[i] = NULL; + break; } - else break; } + mutex_unlock(redirect_mutex); if (del_entry) - { - if (redirection_cache_rear == del_entry) - { - redirection_cache_rear = del_entry->prev; - } - del_entry->prev->next = del_entry->next; + free(del_entry); +} - if (del_entry->next) - { - del_entry->next->prev = del_entry->prev; - } +struct Redirection_Entry* allocate_redirection_entry(char* key, char* host, char* user, unsigned int port) { + struct Redirection_Entry* entry = NULL; + char* key_str; + char* host_str; + char* user_str; + size_t key_len = strlen(key); + size_t host_len = strlen(host); + size_t user_len = strlen(user); - redirection_cache_num--; + if (!ma_multi_malloc(0, + &entry, sizeof(struct Redirection_Entry), + &key_str, key_len + 1, + &host_str, host_len + 1, + &user_str, user_len + 1, + NULL)) + { + return NULL; } - mutex_unlock(redirect_mutex); + ma_strmake(key_str, key, key_len); + ma_strmake(host_str, host, host_len); + ma_strmake(user_str, user, user_len); + (entry)->key = key_str; + (entry)->host = host_str; + (entry)->user = user_str; + (entry)->port = port; + (entry)->last_used = time(NULL); - if (del_entry) - free(del_entry); + return entry; } #ifdef PLUGIN_DYNAMIC