Merge branch '2.1-ipv6' into develop
This commit is contained in:
@ -53,7 +53,7 @@ static int get_users(SERV_LISTENER *listener);
|
||||
static MYSQL *gw_mysql_init(void);
|
||||
static int gw_mysql_set_timeouts(MYSQL* handle);
|
||||
static char *mysql_format_user_entry(void *data);
|
||||
static bool get_hostname(const char *ip_address, char *client_hostname);
|
||||
static bool get_hostname(DCB *dcb, char *client_hostname, size_t size);
|
||||
|
||||
static char* get_new_users_query(const char *server_version, bool include_root)
|
||||
{
|
||||
@ -208,7 +208,8 @@ int validate_mysql_user(sqlite3 *handle, DCB *dcb, MYSQL_session *session,
|
||||
* as a last resort so we avoid the high cost of the DNS lookup.
|
||||
*/
|
||||
char client_hostname[MYSQL_HOST_MAXLEN];
|
||||
get_hostname(dcb->remote, client_hostname);
|
||||
get_hostname(dcb, client_hostname, sizeof(client_hostname) - 1);
|
||||
|
||||
sprintf(sql, mysqlauth_validate_user_query, session->user, client_hostname,
|
||||
client_hostname, session->db, session->db);
|
||||
|
||||
@ -269,6 +270,69 @@ static bool delete_mysql_users(sqlite3 *handle)
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the hostname is of form a.b.c.d/e.f.g.h where e-h is 255 or 0, replace
|
||||
* the zeros in the first part with '%' and remove the second part. This does
|
||||
* not yet support netmasks completely, but should be sufficient for most
|
||||
* situations. In case of error, the hostname may end in an invalid state, which
|
||||
* will cause an error later on.
|
||||
*
|
||||
* @param host The hostname, which is modified in-place. If merging is unsuccessful,
|
||||
* it may end up garbled.
|
||||
*/
|
||||
static void merge_netmask(char *host)
|
||||
{
|
||||
char *delimiter_loc = strchr(host, '/');
|
||||
if (delimiter_loc == NULL)
|
||||
{
|
||||
return; // Nothing to do
|
||||
}
|
||||
/* If anything goes wrong, we put the '/' back in to ensure the hostname
|
||||
* cannot be used.
|
||||
*/
|
||||
*delimiter_loc = '\0';
|
||||
|
||||
char *ip_token_loc = host;
|
||||
char *mask_token_loc = delimiter_loc + 1; // This is at minimum a \0
|
||||
|
||||
while (ip_token_loc && mask_token_loc)
|
||||
{
|
||||
if (strncmp(mask_token_loc, "255", 3) == 0)
|
||||
{
|
||||
// Skip
|
||||
}
|
||||
else if (*mask_token_loc == '0' && *ip_token_loc == '0')
|
||||
{
|
||||
*ip_token_loc = '%';
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Any other combination is considered invalid. This may leave the
|
||||
* hostname in a partially modified state.
|
||||
* TODO: handle more cases
|
||||
*/
|
||||
*delimiter_loc = '/';
|
||||
MXS_ERROR("Unrecognized IP-bytes in host/mask-combination. "
|
||||
"Merge incomplete: %s", host);
|
||||
return;
|
||||
}
|
||||
|
||||
ip_token_loc = strchr(ip_token_loc, '.');
|
||||
mask_token_loc = strchr(mask_token_loc, '.');
|
||||
if (ip_token_loc && mask_token_loc)
|
||||
{
|
||||
ip_token_loc++;
|
||||
mask_token_loc++;
|
||||
}
|
||||
}
|
||||
if (ip_token_loc || mask_token_loc)
|
||||
{
|
||||
*delimiter_loc = '/';
|
||||
MXS_ERROR("Unequal number of IP-bytes in host/mask-combination. "
|
||||
"Merge incomplete: %s", host);
|
||||
}
|
||||
}
|
||||
|
||||
void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
|
||||
const char *db, bool anydb, const char *pw)
|
||||
{
|
||||
@ -611,30 +675,27 @@ bool check_service_permissions(SERVICE* service)
|
||||
*
|
||||
* @return True if the hostname query was successful
|
||||
*/
|
||||
static bool get_hostname(const char *ip_address, char *client_hostname)
|
||||
static bool get_hostname(DCB *dcb, char *client_hostname, size_t size)
|
||||
{
|
||||
/* Looks like the parameters are valid. First, convert the client IP string
|
||||
* to binary form. This is somewhat silly, since just a while ago we had the
|
||||
* binary address but had to zero it. dbusers.c should be refactored to fix this.
|
||||
*/
|
||||
struct sockaddr_in bin_address;
|
||||
bin_address.sin_family = AF_INET;
|
||||
if (inet_pton(bin_address.sin_family, ip_address, &(bin_address.sin_addr)) != 1)
|
||||
struct addrinfo *ai = NULL, hint = {};
|
||||
hint.ai_flags = AI_ALL;
|
||||
int rc;
|
||||
|
||||
if ((rc = getaddrinfo(dcb->remote, NULL, &hint, &ai)) != 0)
|
||||
{
|
||||
MXS_ERROR("Could not convert to binary ip-address: '%s'.", ip_address);
|
||||
MXS_ERROR("Failed to obtain address for host %s, %s",
|
||||
dcb->remote, gai_strerror(rc));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to lookup the domain name of the given IP-address. This is a slow
|
||||
* i/o-operation, which will stall the entire thread. TODO: cache results
|
||||
* if this feature is used often.
|
||||
*/
|
||||
MXS_DEBUG("Resolving '%s'", ip_address);
|
||||
int lookup_result = getnameinfo((struct sockaddr*)&bin_address,
|
||||
sizeof(struct sockaddr_in),
|
||||
client_hostname, sizeof(client_hostname),
|
||||
* if this feature is used often. */
|
||||
int lookup_result = getnameinfo(ai->ai_addr, ai->ai_addrlen,
|
||||
client_hostname, size,
|
||||
NULL, 0, // No need for the port
|
||||
NI_NAMEREQD); // Text address only
|
||||
freeaddrinfo(ai);
|
||||
|
||||
if (lookup_result != 0)
|
||||
{
|
||||
@ -646,7 +707,7 @@ static bool get_hostname(const char *ip_address, char *client_hostname)
|
||||
MXS_DEBUG("IP-lookup success, hostname is: '%s'", client_hostname);
|
||||
}
|
||||
|
||||
return false;
|
||||
return lookup_result == 0;
|
||||
}
|
||||
|
||||
void start_sqlite_transaction(sqlite3 *handle)
|
||||
@ -695,8 +756,6 @@ int get_users_from_server(MYSQL *con, SERVER_REF *server, SERVICE *service, SERV
|
||||
{
|
||||
start_sqlite_transaction(instance->handle);
|
||||
|
||||
/** Delete the old users */
|
||||
delete_mysql_users(instance->handle);
|
||||
MYSQL_ROW row;
|
||||
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
@ -706,6 +765,11 @@ int get_users_from_server(MYSQL *con, SERVER_REF *server, SERVICE *service, SERV
|
||||
strip_escape_chars(row[2]);
|
||||
}
|
||||
|
||||
if (strchr(row[1], '/'))
|
||||
{
|
||||
merge_netmask(row[1]);
|
||||
}
|
||||
|
||||
add_mysql_user(instance->handle, row[0], row[1], row[2],
|
||||
row[3] && strcmp(row[3], "Y") == 0, row[4]);
|
||||
users++;
|
||||
@ -786,6 +850,10 @@ static int get_users(SERV_LISTENER *listener)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Delete the old users */
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)listener->auth_instance;
|
||||
delete_mysql_users(instance->handle);
|
||||
|
||||
SERVER_REF *server = service->dbref;
|
||||
int total_users = -1;
|
||||
|
||||
|
@ -106,18 +106,19 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
return &info;
|
||||
}
|
||||
|
||||
static void get_database_path(SERV_LISTENER *port, char *dest)
|
||||
static void get_database_path(SERV_LISTENER *port, char *dest, size_t size)
|
||||
{
|
||||
MYSQL_AUTH *instance = port->auth_instance;
|
||||
SERVICE *service = port->service;
|
||||
ss_dassert(size - sizeof(DBUSERS_FILE) - 1 >= 0);
|
||||
|
||||
if (instance->cache_dir)
|
||||
{
|
||||
snprintf(dest, sizeof(dest) - sizeof(DBUSERS_FILE) - 1, "%s/", instance->cache_dir);
|
||||
snprintf(dest, size, "%s/", instance->cache_dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(dest, "%s/%s/%s/%s/", get_cachedir(), service->name, port->name, DBUSERS_DIR);
|
||||
snprintf(dest, size, "%s/%s/%s/%s/", get_cachedir(), service->name, port->name, DBUSERS_DIR);
|
||||
}
|
||||
|
||||
if (mxs_mkdir_all(dest, S_IRWXU))
|
||||
@ -254,6 +255,30 @@ static void mysql_auth_destroy(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_localhost_address(struct sockaddr_storage *addr)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
if (addr->ss_family == AF_INET)
|
||||
{
|
||||
struct sockaddr_in *ip = (struct sockaddr_in*)addr;
|
||||
if (ip->sin_addr.s_addr == INADDR_LOOPBACK)
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
else if (addr->ss_family == AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 *ip = (struct sockaddr_in6*)addr;
|
||||
if (memcmp(&ip->sin6_addr, &in6addr_loopback, sizeof(ip->sin6_addr)) == 0)
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Authenticates a MySQL user who is a client to MaxScale.
|
||||
*
|
||||
@ -325,13 +350,13 @@ mysql_auth_authenticate(DCB *dcb)
|
||||
else if (dcb->service->log_auth_warnings)
|
||||
{
|
||||
MXS_WARNING("%s: login attempt for user '%s'@%s:%d, authentication failed.",
|
||||
dcb->service->name, client_data->user, dcb->remote, ntohs(dcb->ipv4.sin_port));
|
||||
if (dcb->ipv4.sin_addr.s_addr == 0x0100007F &&
|
||||
dcb->service->name, client_data->user, dcb->remote, dcb_get_port(dcb));
|
||||
|
||||
if (is_localhost_address(&dcb->ip) &&
|
||||
!dcb->service->localhost_match_wildcard_host)
|
||||
{
|
||||
MXS_NOTICE("If you have a wildcard grant that covers"
|
||||
" this address, try adding "
|
||||
"'localhost_match_wildcard_host=true' for "
|
||||
MXS_NOTICE("If you have a wildcard grant that covers this address, "
|
||||
"try adding 'localhost_match_wildcard_host=true' for "
|
||||
"service '%s'. ", dcb->service->name);
|
||||
}
|
||||
}
|
||||
@ -374,7 +399,7 @@ mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
if (auth_ses->handle == NULL)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
get_database_path(dcb->listener, path);
|
||||
get_database_path(dcb->listener, path, sizeof(path));
|
||||
|
||||
if (!open_client_database(path, &auth_ses->handle))
|
||||
{
|
||||
@ -591,7 +616,7 @@ static int mysql_auth_load_users(SERV_LISTENER *port)
|
||||
if (instance->handle == NULL)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
get_database_path(port, path);
|
||||
get_database_path(port, path, sizeof(path));
|
||||
if (!open_instance_database(path, &instance->handle))
|
||||
{
|
||||
return MXS_AUTH_LOADUSERS_FATAL;
|
||||
|
Reference in New Issue
Block a user