diff options
Diffstat (limited to 'sql/hostname.cc')
-rw-r--r-- | sql/hostname.cc | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/sql/hostname.cc b/sql/hostname.cc new file mode 100644 index 00000000000..0e8d6e36f0f --- /dev/null +++ b/sql/hostname.cc @@ -0,0 +1,235 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + Get hostname for an IP. Hostnames are checked with reverse name lookup and + checked that they doesn't resemble an ip. +*/ + +#include "mysql_priv.h" +#include "hash_filo.h" +#include <m_ctype.h> +#ifdef __cplusplus +extern "C" { // Because of SCO 3.2V4.2 +#endif +#ifndef __WIN__ +#include <sys/resource.h> +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif +#include <netdb.h> +#include <sys/utsname.h> +#endif // __WIN__ +#ifdef __cplusplus +} +#endif + + +class host_entry :public hash_filo_element +{ +public: + char ip[sizeof(((struct in_addr *) 0)->s_addr)]; + uint errors; + char *hostname; +}; + +static hash_filo *hostname_cache; + +void hostname_cache_refresh() +{ + hostname_cache->clear(); +} + +bool hostname_cache_init() +{ + if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE,offsetof(host_entry,ip), + sizeof(struct in_addr),NULL, + (void (*)(void*)) free))) + return 1; + hostname_cache->clear(); + return 0; +} + +void hostname_cache_free() +{ + delete hostname_cache; +} + +static void add_hostname(struct in_addr *in,const char *name) +{ + if (!(specialflag & SPECIAL_NO_HOST_CACHE)) + { + VOID(pthread_mutex_lock(&hostname_cache->lock)); + host_entry *entry; + if (!(entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0))) + { + uint length=name ? strlen(name) : 0; + + if ((entry=(host_entry*) malloc(sizeof(host_entry)+length+1))) + { + char *new_name= (char *) (entry+1); + memcpy_fixed(&entry->ip, &in->s_addr, sizeof(in->s_addr)); + memcpy(new_name, name, length); // Should work even if name == NULL + new_name[length]=0; // End of string + entry->hostname=new_name; + entry->errors=0; + (void) hostname_cache->add(entry); + } + } + VOID(pthread_mutex_unlock(&hostname_cache->lock)); + } +} + + +static inline void add_wrong_ip(struct in_addr *in) +{ + add_hostname(in,NullS); +} + +void inc_host_errors(struct in_addr *in) +{ + VOID(pthread_mutex_lock(&hostname_cache->lock)); + host_entry *entry; + if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0))) + entry->errors++; + VOID(pthread_mutex_unlock(&hostname_cache->lock)); +} + +void reset_host_errors(struct in_addr *in) +{ + VOID(pthread_mutex_lock(&hostname_cache->lock)); + host_entry *entry; + if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0))) + entry->errors=0; + VOID(pthread_mutex_unlock(&hostname_cache->lock)); +} + + +my_string ip_to_hostname(struct in_addr *in, uint *errors) +{ + host_entry *entry; + DBUG_ENTER("ip_to_hostname"); + + /* Check first if we have name in cache */ + *errors=0; + if (!(specialflag & SPECIAL_NO_HOST_CACHE)) + { + VOID(pthread_mutex_lock(&hostname_cache->lock)); + if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0))) + { + char *name; + if (!entry->hostname) + name=0; // Don't allow connection + else + name=my_strdup(entry->hostname,MYF(0)); + *errors= entry->errors; + VOID(pthread_mutex_unlock(&hostname_cache->lock)); + DBUG_RETURN(name); + } + VOID(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))) + { + DBUG_PRINT("error",("gethostbyname_r returned %d",tmp_errno)); + add_wrong_ip(in); + DBUG_RETURN(0); + } + if (!hp->h_name[0]) + { + DBUG_PRINT("error",("Got an empty hostname")); + add_wrong_ip(in); + DBUG_RETURN(0); // Don't allow empty hostnames + } + if (!(name=my_strdup(hp->h_name,MYF(0)))) + DBUG_RETURN(0); // out of memory + +#else + VOID(pthread_mutex_lock(&hostname_cache->lock)); + if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET))) + { + DBUG_PRINT("error",("gethostbyaddr returned %d",errno)); + VOID(pthread_mutex_unlock(&hostname_cache->lock)); + add_wrong_ip(in); + DBUG_RETURN(0); + } + if (!hp->h_name[0]) + { + VOID(pthread_mutex_unlock(&hostname_cache->lock)); + DBUG_PRINT("error",("Got an empty hostname")); + add_wrong_ip(in); + DBUG_RETURN(0); // Don't allow empty hostnames + } + if (!(name=my_strdup(hp->h_name,MYF(0)))) + DBUG_RETURN(0); // out of memory + check=gethostbyname(name); + VOID(pthread_mutex_unlock(&hostname_cache->lock)); + if (!check) + { + DBUG_PRINT("error",("gethostbyname returned %d",errno)); + my_free(name,MYF(0)); + DBUG_RETURN(0); + } +#endif + + /* Don't accept hostnames that starts with digits because they may be + false ip:s */ + if (isdigit(name[0])) + { + char *pos; + for (pos= name+1 ; isdigit(*pos); pos++) ; + if (*pos == '.') + { + DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'")); + my_free(name,MYF(0)); + add_wrong_ip(in); + DBUG_RETURN(0); + } + } + + /* Check that 'gethostbyname' returned the used ip */ + for (uint i=0; check->h_addr_list[i]; i++) + { + if (*(uint32*)(check->h_addr_list)[i] == in->s_addr) + { + add_hostname(in,name); + DBUG_RETURN(name); + } + } + DBUG_PRINT("error",("Couldn't verify hostname with gethostbyname")); + my_free(name,MYF(0)); + add_wrong_ip(in); + DBUG_RETURN(0); +} |