summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorAlexander Nozdrin <alik@sun.com>2009-11-25 13:53:23 +0300
committerAlexander Nozdrin <alik@sun.com>2009-11-25 13:53:23 +0300
commit7eb84da890698c3109795742c6f34d4580567b17 (patch)
treee44eeb900770af5a8a6c8712d985329fd1d7ba44 /sql
parent027a2fff1573550baae84ff97b962169a4961518 (diff)
downloadmariadb-git-7eb84da890698c3109795742c6f34d4580567b17.tar.gz
Backport of WL#798 (MySQL IPv6 support) from 6.0.
The following 6.0 revisions were analyzed: - sp1r-brian@zim.(none)-20071228102738-21894 - sp1r-brian@zim.(none)-20071228121841-56447 - sp1r-brian@zim.(none)-20071228205403-56423 - sp1r-brian@zim.(none)-20071228221139-55341 - sp1r-brian@zim.(none)-20071228233443-55352 - sp1r-brian@zim.(none)-20071229094527-61763 - sp1r-brian@zim.(none)-20071230203739-61746 - sp1r-brian@zim.(none)-20080102213805-61741 - sp1r-brian@zim.(none)-20080103201041-61746 - sp1r-brian@zim.(none)-20080104234927-59875 - sp1r-brian@zim.(none)-20080105005827-59874 - sp1r-brian@zim.(none)-20080105012020-59865 - sp1r-brian@zim.(none)-20080106003858-59857 - sp1r-brian@zim.(none)-20080123195552-31680 - sp1r-brian@zim.(none)-20080124201848-29999 - sp1r-brian@zim.(none)-20080129075144-36991 - sp1r-cbell/Chuck@mysql_cab_desk.-20080215041806-21954 - sp1r-vvaintroub@wva.-20080212124547-06272 - sp1r-dfischer/mysqldev@mysql.com/production.mysql.com-20071223184258-15140 - sp1r-brian@zim.(none)-20080206122216-35774 - sp1r-baker@bk-internal.mysql.com-20080209005622-35947 - sp1r-baker@bk-internal.mysql.com-20080224215608-24613 - sp1r-baker@bk-internal.mysql.com-20080307170710-63543 - sp1r-baker@bk-internal.mysql.com-20080312233205-13069 - sp1r-Reggie@core.-20080402175211-28643 - kpettersson@mysql.com-20080901101150-4ne74r8v0492vv42 - alik@sun.com-20090805173811-9fzt0ymcp9tsvn7k - alik@sun.com-20090805173937-fcv1fdveodq5x9gb - alik@sun.com-20090805175009-g1od16i3t1xkw2qr - kostja@sun.com-20090805200643-j9i4fy7ii8ijho5c - alik@sun.com-20090807195303-j4fb5m4l1dgdahwo - alik@sun.com-20090808114848-3rkzr9kifrijzaqy - alik@sun.com-20090810041739-ugmx34h34uid8mox - alik@sun.com-20090810105306-rf43rfyzzblsy5e7 - alik@sun.com-20090810123113-ccdjwai68b5woqdm - alik@sun.com-20090811080423-gb7pibec1znaydzy - alik@sun.com-20090811082130-5uckar1vx3kdsw7g - alik@sun.com-20090812202051-uqkfwwxxcjvo5ean The following bugfixes are also backported within this patch: - Bug#34292: netdb.h missing in hostname.cc - Bug#39153: Failing to lookup a host name can lead to crash in current IPv6 implementation - Bug#38247: Server does not resolve connecting ip's - Bug#43006: main.skip_name_resolve fails on Windows in PB2 - Bug#45606: ACL requires IPv4-mapped addresses to be used - Bug#45584: Host name cache does not work as a cache
Diffstat (limited to 'sql')
-rw-r--r--sql/client_settings.h4
-rw-r--r--sql/hostname.cc593
-rw-r--r--sql/mysql_priv.h12
-rw-r--r--sql/mysqld.cc114
-rw-r--r--sql/sql_acl.cc81
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_connect.cc43
7 files changed, 615 insertions, 233 deletions
diff --git a/sql/client_settings.h b/sql/client_settings.h
index fd50bfdbb88..7d103d5904d 100644
--- a/sql/client_settings.h
+++ b/sql/client_settings.h
@@ -30,10 +30,6 @@
#define set_sigpipe(mysql)
#define reset_sigpipe(mysql)
#define read_user_name(A) {}
-#define mysql_rpl_query_type(A,B) MYSQL_RPL_ADMIN
-#define mysql_master_send_query(A, B, C) 1
-#define mysql_slave_send_query(A, B, C) 1
-#define mysql_rpl_probe(mysql) 0
#undef HAVE_SMEM
#undef _CUSTOMCONFIG_
diff --git a/sql/hostname.cc b/sql/hostname.cc
index b498c548e61..cf35e60061f 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -18,10 +18,10 @@
@file
@brief
- Get hostname for an IP.
+ Get hostname for an IP address.
- Hostnames are checked with reverse name lookup and
- checked that they doesn't resemble an ip.
+ Hostnames are checked with reverse name lookup and checked that they
+ doesn't resemble an IP address.
*/
#include "mysql_priv.h"
@@ -34,24 +34,54 @@ extern "C" { // Because of SCO 3.2V4.2
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
-#include <netdb.h>
#include <sys/utsname.h>
#endif // __WIN__
#ifdef __cplusplus
}
#endif
+/*
+ HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache.
+*/
+
+#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
+
+/**
+ An entry in the hostname hash table cache.
+
+ Host name cache does two things:
+ - caches host names to save DNS look ups;
+ - counts connect errors from IP.
-class host_entry :public hash_filo_element
+ Host name can be NULL (that means DNS look up failed), but connect errors
+ still are counted.
+*/
+
+class Host_entry :public hash_filo_element
{
public:
- char ip[sizeof(((struct in_addr *) 0)->s_addr)];
- uint errors;
- char *hostname;
+ /**
+ Client IP address. This is the key used with the hash table.
+
+ The client IP address is always expressed in IPv6, even when the
+ network IPv6 stack is not present.
+
+ This IP address is never used to connect to a socket.
+ */
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+
+ /**
+ Number of errors during handshake phase from the IP address.
+ */
+ uint connect_errors;
+
+ /**
+ One of the host names for the IP address. May be NULL.
+ */
+ const char *hostname;
};
static hash_filo *hostname_cache;
-static pthread_mutex_t LOCK_hostname;
void hostname_cache_refresh()
{
@@ -60,219 +90,468 @@ void hostname_cache_refresh()
bool hostname_cache_init()
{
- host_entry tmp;
- uint offset= (uint) ((char*) (&tmp.ip) - (char*) &tmp);
- if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset,
- sizeof(struct in_addr),NULL,
- (my_hash_free_key) free,
- &my_charset_bin)))
+ Host_entry tmp;
+ uint key_offset= (uint) ((char*) (&tmp.ip_key) - (char*) &tmp);
+
+ if (!(hostname_cache= new hash_filo(HOST_CACHE_SIZE,
+ key_offset, HOST_ENTRY_KEY_SIZE,
+ NULL, (my_hash_free_key) free,
+ &my_charset_bin)))
return 1;
+
hostname_cache->clear();
- (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW);
+
return 0;
}
void hostname_cache_free()
{
- if (hostname_cache)
- {
- (void) pthread_mutex_destroy(&LOCK_hostname);
- delete hostname_cache;
- hostname_cache= 0;
- }
+ delete hostname_cache;
+ hostname_cache= NULL;
}
+static void prepare_hostname_cache_key(const char *ip_string,
+ char *ip_key)
+{
+ int ip_string_length= strlen(ip_string);
+ DBUG_ASSERT(ip_string_length < HOST_ENTRY_KEY_SIZE);
-static void add_hostname(struct in_addr *in,const char *name)
+ memset(ip_key, 0, HOST_ENTRY_KEY_SIZE);
+ memcpy_fixed(ip_key, ip_string, ip_string_length);
+}
+
+static inline Host_entry *hostname_cache_search(const char *ip_key)
{
- if (!(specialflag & SPECIAL_NO_HOST_CACHE))
+ return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
+}
+
+static bool add_hostname_impl(const char *ip_key, const char *hostname)
+{
+ if (hostname_cache_search(ip_key))
+ return FALSE;
+
+ size_t hostname_size= hostname ? strlen(hostname) + 1 : 0;
+
+ Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) + hostname_size);
+
+ if (!entry)
+ return TRUE;
+
+ char *hostname_copy;
+
+ memcpy_fixed(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
+
+ if (hostname_size)
{
- pthread_mutex_lock(&hostname_cache->lock);
- host_entry *entry;
- if (!(entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
- {
- uint length=name ? (uint) strlen(name) : 0;
+ hostname_copy= (char *) (entry + 1);
+ memcpy(hostname_copy, hostname, hostname_size);
- if ((entry=(host_entry*) malloc(sizeof(host_entry)+length+1)))
- {
- char *new_name;
- memcpy_fixed(&entry->ip, &in->s_addr, sizeof(in->s_addr));
- if (length)
- memcpy(new_name= (char *) (entry+1), name, length+1);
- else
- new_name=0;
- entry->hostname=new_name;
- entry->errors=0;
- (void) hostname_cache->add(entry);
- }
- }
- pthread_mutex_unlock(&hostname_cache->lock);
+ DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
+ (const char *) ip_key,
+ (const char *) hostname_copy));
}
-}
+ else
+ {
+ hostname_copy= NULL;
+
+ DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
+ (const char *) ip_key));
+ }
+
+ entry->hostname= hostname_copy;
+ entry->connect_errors= 0;
+ return hostname_cache->add(entry);
+}
-inline void add_wrong_ip(struct in_addr *in)
+static bool add_hostname(const char *ip_key, const char *hostname)
{
- add_hostname(in,NullS);
+ if (specialflag & SPECIAL_NO_HOST_CACHE)
+ return FALSE;
+
+ pthread_mutex_lock(&hostname_cache->lock);
+
+ bool err_status= add_hostname_impl(ip_key, hostname);
+
+ pthread_mutex_unlock(&hostname_cache->lock);
+
+ return err_status;
}
-void inc_host_errors(struct in_addr *in)
+void inc_host_errors(const char *ip_string)
{
+ if (!ip_string)
+ return;
+
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+ prepare_hostname_cache_key(ip_string, ip_key);
+
pthread_mutex_lock(&hostname_cache->lock);
- host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
- entry->errors++;
+
+ Host_entry *entry= hostname_cache_search(ip_key);
+
+ if (entry)
+ entry->connect_errors++;
+
pthread_mutex_unlock(&hostname_cache->lock);
}
-void reset_host_errors(struct in_addr *in)
+
+void reset_host_errors(const char *ip_string)
{
+ if (!ip_string)
+ return;
+
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+ prepare_hostname_cache_key(ip_string, ip_key);
+
pthread_mutex_lock(&hostname_cache->lock);
- host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
- entry->errors=0;
+
+ Host_entry *entry= hostname_cache_search(ip_key);
+
+ if (entry)
+ entry->connect_errors= 0;
+
pthread_mutex_unlock(&hostname_cache->lock);
}
-/* Deal with systems that don't defined INADDR_LOOPBACK */
-#ifndef INADDR_LOOPBACK
-#define INADDR_LOOPBACK 0x7f000001UL
-#endif
-char * ip_to_hostname(struct in_addr *in, uint *errors)
+static inline bool is_ip_loopback(const struct sockaddr *ip)
+{
+ switch (ip->sa_family) {
+ case AF_INET:
+ {
+ /* Check for IPv4 127.0.0.1. */
+ struct in_addr *ip4= &((struct sockaddr_in *) ip)->sin_addr;
+ return ntohl(ip4->s_addr) == INADDR_LOOPBACK;
+ }
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ {
+ /* Check for IPv6 ::1. */
+ struct in6_addr *ip6= &((struct sockaddr_in6 *) ip)->sin6_addr;
+ return IN6_IS_ADDR_LOOPBACK(ip6);
+ }
+#endif /* HAVE_IPV6 */
+
+ default:
+ return FALSE;
+ }
+}
+
+static inline bool is_hostname_valid(const char *hostname)
{
- uint i;
- host_entry *entry;
+ /*
+ A hostname is invalid if it starts with a number followed by a dot
+ (IPv4 address).
+ */
+
+ if (!my_isdigit(&my_charset_latin1, hostname[0]))
+ return TRUE;
+
+ const char *p= hostname + 1;
+
+ while (my_isdigit(&my_charset_latin1, *p))
+ ++p;
+
+ return *p != '.';
+}
+
+/**
+ Resolve IP-address to host name.
+
+ This function does the following things:
+ - resolves IP-address;
+ - employs Forward Confirmed Reverse DNS technique to validate IP-address;
+ - returns host name if IP-address is validated;
+ - set value to out-variable connect_errors -- this variable represents the
+ number of connection errors from the specified IP-address.
+
+ NOTE: connect_errors are counted (are supported) only for the clients
+ where IP-address can be resolved and FCrDNS check is passed.
+
+ @param [in] ip_storage IP address (sockaddr). Must be set.
+ @param [in] ip_string IP address (string). Must be set.
+ @param [out] hostname
+ @param [out] connect_errors
+
+ @return Error status
+ @retval FALSE Success
+ @retval TRUE Error
+
+ The function does not set/report MySQL server error in case of failure.
+ It's caller's responsibility to handle failures of this function
+ properly.
+*/
+
+bool ip_to_hostname(struct sockaddr_storage *ip_storage,
+ const char *ip_string,
+ char **hostname, uint *connect_errors)
+{
+ const struct sockaddr *ip= (const sockaddr *) ip_storage;
+ int err_code;
+ bool err_status;
+
DBUG_ENTER("ip_to_hostname");
- *errors=0;
+ DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
+ (const char *) ip_string,
+ (int) ip->sa_family));
- /* We always treat the loopback address as "localhost". */
- if (in->s_addr == htonl(INADDR_LOOPBACK)) // is expanded inline by gcc
- DBUG_RETURN((char *)my_localhost);
+ /* Check if we have loopback address (127.0.0.1 or ::1). */
+
+ if (is_ip_loopback(ip))
+ {
+ DBUG_PRINT("info", ("Loopback address detected."));
+
+ *connect_errors= 0; /* Do not count connect errors from localhost. */
+ *hostname= (char *) my_localhost;
+
+ DBUG_RETURN(FALSE);
+ }
+
+ /* Prepare host name cache key. */
+
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+ prepare_hostname_cache_key(ip_string, ip_key);
+
+ /* Check first if we have host name in the cache. */
- /* Check first if we have name in cache */
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
{
pthread_mutex_lock(&hostname_cache->lock);
- if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
+
+ Host_entry *entry= hostname_cache_search(ip_key);
+
+ if (entry)
{
- char *name;
- if (!entry->hostname)
- name=0; // Don't allow connection
- else
- name=my_strdup(entry->hostname,MYF(0));
- *errors= entry->errors;
+ *connect_errors= entry->connect_errors;
+ *hostname= NULL;
+
+ if (entry->hostname)
+ *hostname= my_strdup(entry->hostname, MYF(0));
+
+ DBUG_PRINT("info",("IP (%s) has been found in the cache. "
+ "Hostname: '%s'; connect_errors: %d",
+ (const char *) ip_key,
+ (const char *) (*hostname? *hostname : "null"),
+ (int) *connect_errors));
+
pthread_mutex_unlock(&hostname_cache->lock);
- DBUG_RETURN(name);
+
+ DBUG_RETURN(FALSE);
}
+
pthread_mutex_unlock(&hostname_cache->lock);
}
- struct hostent *hp, *check;
- char *name;
- LINT_INIT(check);
-#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
- char buff[GETHOSTBYADDR_BUFF_SIZE],buff2[GETHOSTBYNAME_BUFF_SIZE];
- int tmp_errno;
- struct hostent tmp_hostent, tmp_hostent2;
-#ifdef HAVE_purify
- bzero(buff,sizeof(buff)); // Bug in purify
-#endif
- if (!(hp=gethostbyaddr_r((char*) in,sizeof(*in),
- AF_INET,
- &tmp_hostent,buff,sizeof(buff),&tmp_errno)))
- {
- DBUG_PRINT("error",("gethostbyaddr_r returned %d",tmp_errno));
- return 0;
- }
- if (!(check=my_gethostbyname_r(hp->h_name,&tmp_hostent2,buff2,sizeof(buff2),
- &tmp_errno)))
+ /*
+ Resolve host name. Return an error if a host name can not be resolved
+ (instead of returning the numeric form of the host name).
+ */
+
+ char hostname_buffer[NI_MAXHOST];
+
+ DBUG_PRINT("info", ("Resolving '%s'...", (const char *) ip_key));
+
+ err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0,
+ NI_NAMEREQD);
+
+ if (err_code == EAI_NONAME)
{
- DBUG_PRINT("error",("gethostbyname_r returned %d",tmp_errno));
/*
- Don't cache responses when the DSN server is down, as otherwise
- transient DNS failure may leave any number of clients (those
- that attempted to connect during the outage) unable to connect
- indefinitely.
+ There is no reverse address mapping for the IP address. A host name
+ can not be resolved.
*/
- if (tmp_errno == HOST_NOT_FOUND || tmp_errno == NO_DATA)
- add_wrong_ip(in);
- my_gethostbyname_r_free();
- DBUG_RETURN(0);
- }
- if (!hp->h_name[0])
- {
- DBUG_PRINT("error",("Got an empty hostname"));
- add_wrong_ip(in);
- my_gethostbyname_r_free();
- DBUG_RETURN(0); // Don't allow empty hostnames
- }
- if (!(name=my_strdup(hp->h_name,MYF(0))))
- {
- my_gethostbyname_r_free();
- DBUG_RETURN(0); // out of memory
+
+ DBUG_PRINT("error", ("IP address '%s' could not be resolved: "
+ "no reverse address mapping.",
+ (const char *) ip_key));
+
+ sql_print_warning("IP address '%s' could not be resolved: "
+ "no reverse address mapping.",
+ (const char *) ip_key);
+
+ err_status= add_hostname(ip_key, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
+
+ DBUG_RETURN(err_status);
}
- my_gethostbyname_r_free();
-#else
- pthread_mutex_lock(&LOCK_hostname);
- if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET)))
+ else if (err_code)
{
- pthread_mutex_unlock(&LOCK_hostname);
- DBUG_PRINT("error",("gethostbyaddr returned %d",errno));
+ DBUG_PRINT("error", ("IP address '%s' could not be resolved: "
+ "getnameinfo() returned %d.",
+ (const char *) ip_key,
+ (int) err_code));
+
+ sql_print_warning("IP address '%s' could not be resolved: "
+ "getnameinfo() returned error (code: %d).",
+ (const char *) ip_key,
+ (int) err_code);
- if (errno == HOST_NOT_FOUND || errno == NO_DATA)
- goto add_wrong_ip_and_return;
- /* Failure, don't cache responce */
- DBUG_RETURN(0);
+ DBUG_RETURN(TRUE);
}
- if (!hp->h_name[0]) // Don't allow empty hostnames
+
+ DBUG_PRINT("info", ("IP '%s' resolved to '%s'.",
+ (const char *) ip_key,
+ (const char *) hostname_buffer));
+
+ /*
+ Validate hostname: the server does not accept host names, which
+ resemble IP addresses.
+
+ The thing is that theoretically, a host name can be in a form of IPv4
+ address (123.example.org, or 1.2 or even 1.2.3.4). We have to deny such
+ host names because ACL-systems is not designed to work with them.
+
+ For example, it is possible to specify a host name mask (like
+ 192.168.1.%) for an ACL rule. Then, if IPv4-like hostnames are allowed,
+ there is a security hole: instead of allowing access for
+ 192.168.1.0/255 network (which was assumed by the user), the access
+ will be allowed for host names like 192.168.1.example.org.
+ */
+
+ if (!is_hostname_valid(hostname_buffer))
{
- pthread_mutex_unlock(&LOCK_hostname);
- DBUG_PRINT("error",("Got an empty hostname"));
- goto add_wrong_ip_and_return;
+ DBUG_PRINT("error", ("IP address '%s' has been resolved "
+ "to the host name '%s', which resembles "
+ "IPv4-address itself.",
+ (const char *) ip_key,
+ (const char *) hostname_buffer));
+
+ sql_print_warning("IP address '%s' has been resolved "
+ "to the host name '%s', which resembles "
+ "IPv4-address itself.",
+ (const char *) ip_key,
+ (const char *) hostname_buffer);
+
+ err_status= add_hostname(ip_key, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
+
+ DBUG_RETURN(err_status);
}
- if (!(name=my_strdup(hp->h_name,MYF(0))))
+
+ /* Get IP-addresses for the resolved host name (FCrDNS technique). */
+
+ struct addrinfo hints;
+ struct addrinfo *addr_info_list;
+
+ memset(&hints, 0, sizeof (struct addrinfo));
+ hints.ai_flags= AI_PASSIVE;
+ hints.ai_socktype= SOCK_STREAM;
+ hints.ai_family= AF_UNSPEC;
+
+ DBUG_PRINT("info", ("Getting IP addresses for hostname '%s'...",
+ (const char *) hostname_buffer));
+
+ err_code= getaddrinfo(hostname_buffer, NULL, &hints, &addr_info_list);
+
+ if (err_code == EAI_NONAME)
{
- pthread_mutex_unlock(&LOCK_hostname);
- DBUG_RETURN(0); // out of memory
+ /*
+ Don't cache responses when the DNS server is down, as otherwise
+ transient DNS failure may leave any number of clients (those
+ that attempted to connect during the outage) unable to connect
+ indefinitely.
+ */
+
+ err_status= add_hostname(ip_key, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
+
+ DBUG_RETURN(err_status);
}
- check=gethostbyname(name);
- pthread_mutex_unlock(&LOCK_hostname);
- if (!check)
+ else if (err_code)
{
- DBUG_PRINT("error",("gethostbyname returned %d",errno));
- my_free(name,MYF(0));
- DBUG_RETURN(0);
+ DBUG_PRINT("error", ("getaddrinfo() failed with error code %d.", err_code));
+ DBUG_RETURN(TRUE);
}
-#endif
- /* Don't accept hostnames that starts with digits because they may be
- false ip:s */
- if (my_isdigit(&my_charset_latin1,name[0]))
+ /* Check that getaddrinfo() returned the used IP (FCrDNS technique). */
+
+ DBUG_PRINT("info", ("The following IP addresses found for '%s':",
+ (const char *) hostname_buffer));
+
+ for (struct addrinfo *addr_info= addr_info_list;
+ addr_info; addr_info= addr_info->ai_next)
{
- char *pos;
- for (pos= name+1 ; my_isdigit(&my_charset_latin1,*pos); pos++) ;
- if (*pos == '.')
+ char ip_buffer[HOST_ENTRY_KEY_SIZE];
+
+ {
+ err_status=
+ vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen,
+ ip_buffer, sizeof (ip_buffer));
+ DBUG_ASSERT(!err_status);
+ }
+
+ DBUG_PRINT("info", (" - '%s'", (const char *) ip_buffer));
+
+ if (strcmp(ip_key, ip_buffer) == 0)
{
- DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'"));
- my_free(name,MYF(0));
- goto add_wrong_ip_and_return;
+ /* Copy host name string to be stored in the cache. */
+
+ *hostname= my_strdup(hostname_buffer, MYF(0));
+
+ if (!*hostname)
+ {
+ DBUG_PRINT("error", ("Out of memory."));
+
+ freeaddrinfo(addr_info_list);
+ DBUG_RETURN(TRUE);
+ }
+
+ break;
}
}
- /* Check that 'gethostbyname' returned the used ip */
- for (i=0; check->h_addr_list[i]; i++)
+ /* Log resolved IP-addresses if no match was found. */
+
+ if (!*hostname)
{
- if (*(uint32*)(check->h_addr_list)[i] == in->s_addr)
+ sql_print_information("Hostname '%s' does not resolve to '%s'.",
+ (const char *) hostname_buffer,
+ (const char *) ip_key);
+ sql_print_information("Hostname '%s' has the following IP addresses:",
+ (const char *) hostname_buffer);
+
+ for (struct addrinfo *addr_info= addr_info_list;
+ addr_info; addr_info= addr_info->ai_next)
{
- add_hostname(in,name);
- DBUG_RETURN(name);
+ char ip_buffer[HOST_ENTRY_KEY_SIZE];
+
+ err_status=
+ vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen,
+ ip_buffer, sizeof (ip_buffer));
+ DBUG_ASSERT(!err_status);
+
+ sql_print_information(" - %s\n", (const char *) ip_buffer);
}
}
- DBUG_PRINT("error",("Couldn't verify hostname with gethostbyname"));
- my_free(name,MYF(0));
-add_wrong_ip_and_return:
- add_wrong_ip(in);
- DBUG_RETURN(0);
+ /* Free the result of getaddrinfo(). */
+
+ freeaddrinfo(addr_info_list);
+
+ /* Add an entry for the IP to the cache. */
+
+ if (*hostname)
+ {
+ err_status= add_hostname(ip_key, *hostname);
+ *connect_errors= 0;
+ }
+ else
+ {
+ DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo()."));
+
+ err_status= add_hostname(ip_key, NULL);
+ *hostname= NULL;
+ *connect_errors= 0;
+ }
+
+ DBUG_RETURN(err_status);
}
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index c6454679e28..552b834b379 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -53,6 +53,9 @@
#include "sql_array.h"
#include "sql_plugin.h"
#include "scheduler.h"
+#ifndef __WIN__
+#include <netdb.h>
+#endif
class Parser_state;
@@ -2235,10 +2238,11 @@ uint build_table_shadow_filename(char *buff, size_t bufflen,
#define FRM_ONLY (1 << 3)
/* from hostname.cc */
-struct in_addr;
-char * ip_to_hostname(struct in_addr *in,uint *errors);
-void inc_host_errors(struct in_addr *in);
-void reset_host_errors(struct in_addr *in);
+bool ip_to_hostname(struct sockaddr_storage *ip_storage,
+ const char *ip_string,
+ char **hostname, uint *connect_errors);
+void inc_host_errors(const char *ip_string);
+void reset_host_errors(const char *ip_string);
bool hostname_cache_init();
void hostname_cache_free();
void hostname_cache_refresh(void);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 1cab245b317..5f00fa0ec64 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -106,7 +106,6 @@ extern "C" { // Because of SCO 3.2V4.2
#ifdef HAVE_SYS_UN_H
# include <sys/un.h>
#endif
-#include <netdb.h>
#ifdef HAVE_SELECT_H
# include <select.h>
#endif
@@ -176,7 +175,7 @@ static void registerwithneb();
static void getvolumename();
static void getvolumeID(BYTE *volumeName);
#endif /* __NETWARE__ */
-
+
#ifdef _AIX41
int initgroups(const char *,unsigned int);
@@ -390,7 +389,6 @@ static my_bool opt_short_log_format= 0;
static uint kill_cached_threads, wake_thread;
static ulong killed_threads, thread_created;
static ulong max_used_connections;
-static ulong my_bind_addr; /**< the address we bind to */
static volatile ulong cached_thread_count= 0;
static const char *sql_mode_str= "OFF";
/* Text representation for OPTIMIZER_SWITCH_DEFAULT */
@@ -1635,17 +1633,18 @@ static void set_root(const char *path)
#endif
}
+
static void network_init(void)
{
- struct sockaddr_in IPaddr;
#ifdef HAVE_SYS_UN_H
struct sockaddr_un UNIXaddr;
#endif
- int arg=1;
+ int arg;
int ret;
uint waited;
uint this_wait;
uint retry;
+ char port_buf[NI_MAXSERV];
DBUG_ENTER("network_init");
LINT_INIT(ret);
@@ -1656,26 +1655,65 @@ static void network_init(void)
if (mysqld_port != 0 && !opt_disable_networking && !opt_bootstrap)
{
+ struct addrinfo *ai, *a;
+ struct addrinfo hints;
+ int error;
DBUG_PRINT("general",("IP Socket is %d",mysqld_port));
- ip_sock = socket(AF_INET, SOCK_STREAM, 0);
+
+ bzero(&hints, sizeof (hints));
+ hints.ai_flags= AI_PASSIVE;
+ hints.ai_socktype= SOCK_STREAM;
+ hints.ai_family= AF_UNSPEC;
+
+ my_snprintf(port_buf, NI_MAXSERV, "%d", mysqld_port);
+ error= getaddrinfo(my_bind_addr_str, port_buf, &hints, &ai);
+ if (error != 0)
+ {
+ DBUG_PRINT("error",("Got error: %d from getaddrinfo()", error));
+ sql_perror(ER(ER_IPSOCK_ERROR)); /* purecov: tested */
+ unireg_abort(1); /* purecov: tested */
+ }
+
+ for (a= ai; a != NULL; a= a->ai_next)
+ {
+ ip_sock= socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ if (ip_sock != INVALID_SOCKET)
+ break;
+ }
+
if (ip_sock == INVALID_SOCKET)
{
DBUG_PRINT("error",("Got error: %d from socket()",socket_errno));
sql_perror(ER(ER_IPSOCK_ERROR)); /* purecov: tested */
unireg_abort(1); /* purecov: tested */
}
- bzero((char*) &IPaddr, sizeof(IPaddr));
- IPaddr.sin_family = AF_INET;
- IPaddr.sin_addr.s_addr = my_bind_addr;
- IPaddr.sin_port = (unsigned short) htons((unsigned short) mysqld_port);
#ifndef __WIN__
/*
We should not use SO_REUSEADDR on windows as this would enable a
user to open two mysqld servers with the same TCP/IP port.
*/
+ arg= 1;
(void) setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,sizeof(arg));
#endif /* __WIN__ */
+
+#ifdef IPV6_V6ONLY
+ /*
+ For interoperability with older clients, IPv6 socket should
+ listen on both IPv6 and IPv4 wildcard addresses.
+ Turn off IPV6_V6ONLY option.
+
+ NOTE: this will work starting from Windows Vista only.
+ On Windows XP dual stack is not available, so it will not
+ listen on the corresponding IPv4-address.
+ */
+ if (a->ai_family == AF_INET6)
+ {
+ arg= 0;
+ (void) setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
+ sizeof(arg));
+ }
+#endif
/*
Sometimes the port is not released fast enough when stopping and
restarting the server. This happens quite often with the test suite
@@ -1686,8 +1724,7 @@ static void network_init(void)
*/
for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
{
- if (((ret= bind(ip_sock, my_reinterpret_cast(struct sockaddr *) (&IPaddr),
- sizeof(IPaddr))) >= 0) ||
+ if (((ret= bind(ip_sock, a->ai_addr, a->ai_addrlen)) >= 0 ) ||
(socket_errno != SOCKET_EADDRINUSE) ||
(waited >= mysqld_port_timeout))
break;
@@ -1695,6 +1732,7 @@ static void network_init(void)
this_wait= retry * retry / 3 + 1;
sleep(this_wait);
}
+ freeaddrinfo(ai);
if (ret < 0)
{
DBUG_PRINT("error",("Got error: %d from bind",socket_errno));
@@ -1716,7 +1754,6 @@ static void network_init(void)
if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap &&
opt_enable_named_pipe)
{
-
strxnmov(pipe_name, sizeof(pipe_name)-1, "\\\\.\\pipe\\",
mysqld_unix_port, NullS);
bzero((char*) &saPipeSecurity, sizeof(saPipeSecurity));
@@ -1782,6 +1819,7 @@ static void network_init(void)
UNIXaddr.sun_family = AF_UNIX;
strmov(UNIXaddr.sun_path, mysqld_unix_port);
(void) unlink(mysqld_unix_port);
+ arg= 1;
(void) setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,
sizeof(arg));
umask(0);
@@ -4850,7 +4888,7 @@ static bool read_init_file(char *file_name)
When we enter this function, LOCK_thread_count is hold!
*/
-
+
void handle_connection_in_main_thread(THD *thd)
{
safe_mutex_assert_owner(&LOCK_thread_count);
@@ -4933,7 +4971,6 @@ void create_thread_to_handle_connection(THD *thd)
static void create_new_thread(THD *thd)
{
- NET *net=&thd->net;
DBUG_ENTER("create_new_thread");
/*
@@ -5003,6 +5040,7 @@ inline void kill_broken_server()
/* Handle new connections and spawn new process to handle them */
#ifndef EMBEDDED_LIBRARY
+
void handle_connections_sockets()
{
my_socket sock,new_sock;
@@ -5010,7 +5048,7 @@ void handle_connections_sockets()
uint max_used_connection= (uint) (max(ip_sock,unix_sock)+1);
fd_set readFDs,clientFDs;
THD *thd;
- struct sockaddr_in cAddr;
+ struct sockaddr_storage cAddr;
int ip_flags=0,socket_flags=0,flags;
st_vio *vio_tmp;
DBUG_ENTER("handle_connections_sockets");
@@ -5084,9 +5122,9 @@ void handle_connections_sockets()
#endif /* NO_FCNTL_NONBLOCK */
for (uint retry=0; retry < MAX_ACCEPT_RETRY; retry++)
{
- size_socket length=sizeof(struct sockaddr_in);
- new_sock = accept(sock, my_reinterpret_cast(struct sockaddr *) (&cAddr),
- &length);
+ size_socket length= sizeof(struct sockaddr_storage);
+ new_sock= accept(sock, (struct sockaddr *)(&cAddr),
+ &length);
#ifdef __NETWARE__
// TODO: temporary fix, waiting for TCP/IP fix - DEFECT000303149
if ((new_sock == INVALID_SOCKET) && (socket_errno == EINVAL))
@@ -5157,9 +5195,10 @@ void handle_connections_sockets()
{
size_socket dummyLen;
- struct sockaddr dummy;
- dummyLen = sizeof(struct sockaddr);
- if (getsockname(new_sock,&dummy, &dummyLen) < 0)
+ struct sockaddr_storage dummy;
+ dummyLen = sizeof(dummy);
+ if ( getsockname(new_sock,(struct sockaddr *)&dummy,
+ (SOCKET_SIZE_TYPE *)&dummyLen) < 0 )
{
sql_perror("Error on new connection socket");
(void) shutdown(new_sock, SHUT_RDWR);
@@ -5505,7 +5544,7 @@ errorconn:
NullS);
sql_perror(buff);
}
- if (handle_client_file_map)
+ if (handle_client_file_map)
CloseHandle(handle_client_file_map);
if (handle_client_map)
UnmapViewOfFile(handle_client_map);
@@ -7786,7 +7825,6 @@ static int mysql_init_variables(void)
strmov(server_version, MYSQL_SERVER_VERSION);
myisam_recover_options_str= sql_mode_str= "OFF";
myisam_stats_method_str= "nulls_unequal";
- my_bind_addr = htonl(INADDR_ANY);
threads.empty();
thread_cache.empty();
key_caches.empty();
@@ -8226,27 +8264,25 @@ mysqld_get_one_option(int optid,
my_use_symdir=0;
break;
case (int) OPT_BIND_ADDRESS:
- if ((my_bind_addr= (ulong) inet_addr(argument)) == INADDR_NONE)
{
- struct hostent *ent;
- if (argument[0])
- ent=gethostbyname(argument);
- else
+ struct addrinfo *res_lst, hints;
+
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_socktype= SOCK_STREAM;
+ hints.ai_protocol= IPPROTO_TCP;
+
+ if (getaddrinfo(argument, NULL, &hints, &res_lst) != 0)
{
- char myhostname[255];
- if (gethostname(myhostname,sizeof(myhostname)) < 0)
- {
- sql_perror("Can't start server: cannot get my own hostname!");
- return 1;
- }
- ent=gethostbyname(myhostname);
+ sql_print_error("Can't start server: cannot resolve hostname!");
+ return 1;
}
- if (!ent)
+
+ if (res_lst->ai_next)
{
- sql_perror("Can't start server: cannot resolve hostname!");
+ sql_print_error("Can't start server: bind-address refers to multiple interfaces!");
return 1;
}
- my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr;
+ freeaddrinfo(res_lst);
}
break;
case (int) OPT_PID_FILE:
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 641423605aa..155e7298e90 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1785,24 +1785,83 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
(ip && !wild_compare(ip, host->hostname, 0)));
}
+/**
+ Check if the given host name needs to be resolved or not.
+ Host name has to be resolved if it actually contains *name*.
+
+ For example:
+ 192.168.1.1 --> FALSE
+ 192.168.1.0/255.255.255.0 --> FALSE
+ % --> FALSE
+ 192.168.1.% --> FALSE
+ AB% --> FALSE
+
+ AAAAFFFF --> TRUE (Hostname)
+ AAAA:FFFF:1234:5678 --> FALSE
+ ::1 --> FALSE
+
+ This function does not check if the given string is a valid host name or
+ not. It assumes that the argument is a valid host name.
+
+ @param hostname the string to check.
+
+ @return a flag telling if the argument needs to be resolved or not.
+ @retval TRUE the argument is a host name and needs to be resolved.
+ @retval FALSE the argument is either an IP address, or a patter and
+ should not be resolved.
+*/
+
bool hostname_requires_resolving(const char *hostname)
{
- char cur;
if (!hostname)
return FALSE;
- size_t namelen= strlen(hostname);
- size_t lhlen= strlen(my_localhost);
- if ((namelen == lhlen) &&
- !my_strnncoll(system_charset_info, (const uchar *)hostname, namelen,
- (const uchar *)my_localhost, strlen(my_localhost)))
+
+ /* Check if hostname is the localhost. */
+
+ size_t hostname_len= strlen(hostname);
+ size_t localhost_len= strlen(my_localhost);
+
+ if (hostname == my_localhost ||
+ (hostname_len == localhost_len &&
+ !my_strnncoll(system_charset_info,
+ (const uchar *) hostname, hostname_len,
+ (const uchar *) my_localhost, strlen(my_localhost))))
+ {
return FALSE;
- for (; (cur=*hostname); hostname++)
+ }
+
+ /*
+ If the string contains any of {':', '%', '_', '/'}, it is definitely
+ not a host name:
+ - ':' means that the string is an IPv6 address;
+ - '%' or '_' means that the string is a pattern;
+ - '/' means that the string is an IPv4 network address;
+ */
+
+ for (const char *p= hostname; *p; ++p)
{
- if ((cur != '%') && (cur != '_') && (cur != '.') && (cur != '/') &&
- ((cur < '0') || (cur > '9')))
- return TRUE;
+ switch (*p) {
+ case ':':
+ case '%':
+ case '_':
+ case '/':
+ return FALSE;
+ }
}
- return FALSE;
+
+ /*
+ Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
+ (12.34.56.78). The assumption is that if the string contains only
+ digits and dots, it is an IPv4 address. Otherwise -- a host name.
+ */
+
+ for (const char *p= hostname; *p; ++p)
+ {
+ if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
+ return TRUE; /* a "letter" has been found. */
+ }
+
+ return FALSE; /* all characters are either dots or digits. */
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 03a92c1a685..551a2372632 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1238,7 +1238,6 @@ public:
HASH user_vars; // hash for user variables
String packet; // dynamic buffer for network I/O
String convert_buffer; // buffer for charset conversions
- struct sockaddr_in remote; // client socket address
struct rand_struct rand; // used for authentication
struct system_variables variables; // Changeable local variables
struct system_status_var status_var; // Per thread statistic vars
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index f5f962a02a3..8ecafce6f93 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -377,7 +377,8 @@ check_user(THD *thd, enum enum_server_command command,
if (send_old_password_request(thd) ||
my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
{
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
DBUG_RETURN(1);
}
@@ -498,9 +499,9 @@ check_user(THD *thd, enum enum_server_command command,
thd->main_security_ctx.host_or_ip,
passwd_len ? ER(ER_YES) : ER(ER_NO));
/*
- log access denied messages to the error log when log-warnings = 2
+ Log access denied messages to the error log when log-warnings = 2
so that the overhead of the general query log is not required to track
- failed connections
+ failed connections.
*/
if (global_system_variables.log_warnings > 1)
{
@@ -666,9 +667,9 @@ static int check_connection(THD *thd)
if (!thd->main_security_ctx.host) // If TCP/IP connection
{
- char ip[30];
+ char ip[NI_MAXHOST];
- if (vio_peer_addr(net->vio, ip, &thd->peer_port))
+ if (vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST))
{
my_error(ER_BAD_HOST_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
@@ -676,12 +677,15 @@ static int check_connection(THD *thd)
if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
return 1; /* The error is set by my_strdup(). */
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
- vio_in_addr(net->vio,&thd->remote.sin_addr);
if (!(specialflag & SPECIAL_NO_RESOLVE))
{
- vio_in_addr(net->vio,&thd->remote.sin_addr);
- thd->main_security_ctx.host=
- ip_to_hostname(&thd->remote.sin_addr, &connect_errors);
+ if (ip_to_hostname(&net->vio->remote, thd->main_security_ctx.ip,
+ &thd->main_security_ctx.host, &connect_errors))
+ {
+ my_error(ER_BAD_HOST_ERROR, MYF(0), ip);
+ return 1;
+ }
+
/* Cut very long hostnames to avoid possible overflows */
if (thd->main_security_ctx.host)
{
@@ -714,7 +718,7 @@ static int check_connection(THD *thd)
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
thd->main_security_ctx.ip= 0;
/* Reset sin_addr */
- bzero((char*) &thd->remote, sizeof(thd->remote));
+ bzero((char*) &net->vio->remote, sizeof(net->vio->remote));
}
vio_keepalive(net->vio, TRUE);
@@ -769,7 +773,8 @@ static int check_connection(THD *thd)
(pkt_len= my_net_read(net)) == packet_error ||
pkt_len < MIN_HANDSHAKE_SIZE)
{
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0),
thd->main_security_ctx.host_or_ip);
return 1;
@@ -779,7 +784,7 @@ static int check_connection(THD *thd)
#include "_cust_sql_parse.h"
#endif
if (connect_errors)
- reset_host_errors(&thd->remote.sin_addr);
+ reset_host_errors(thd->main_security_ctx.ip);
if (thd->packet.alloc(thd->variables.net_buffer_length))
return 1; /* The error is set by alloc(). */
@@ -813,7 +818,7 @@ static int check_connection(THD *thd)
/* Do the SSL layering. */
if (!ssl_acceptor_fd)
{
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
@@ -821,7 +826,8 @@ static int check_connection(THD *thd)
if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
{
DBUG_PRINT("error", ("Failed to accept new SSL connection"));
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
@@ -831,7 +837,8 @@ static int check_connection(THD *thd)
{
DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
pkt_len));
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
@@ -840,7 +847,8 @@ static int check_connection(THD *thd)
if (end >= (char*) net->read_pos+ pkt_len +2)
{
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
@@ -878,7 +886,8 @@ static int check_connection(THD *thd)
if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
{
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}