hostname.cc 7.29 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2006 MySQL AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4
   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
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6

unknown's avatar
unknown committed
7 8 9 10
   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.
unknown's avatar
unknown committed
11

unknown's avatar
unknown committed
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   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
28
#if !defined( __WIN__)
unknown's avatar
unknown committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
#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;
unknown's avatar
unknown committed
49
static pthread_mutex_t LOCK_hostname;
unknown's avatar
unknown committed
50 51 52 53 54 55 56 57

void hostname_cache_refresh()
{
  hostname_cache->clear();
}

bool hostname_cache_init()
{
unknown's avatar
unknown committed
58 59
  host_entry tmp;
  uint offset= (uint) ((char*) (&tmp.ip) - (char*) &tmp);
60
  if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset,
unknown's avatar
unknown committed
61
				     sizeof(struct in_addr),NULL,
unknown's avatar
unknown committed
62
				     (hash_free_key) free,
63
				     &my_charset_bin)))
unknown's avatar
unknown committed
64 65
    return 1;
  hostname_cache->clear();
66
  (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW);
unknown's avatar
unknown committed
67 68 69 70 71
  return 0;
}

void hostname_cache_free()
{
72 73 74 75 76 77
  if (hostname_cache)
  {
    (void) pthread_mutex_destroy(&LOCK_hostname);
    delete hostname_cache;
    hostname_cache= 0;
  }
unknown's avatar
unknown committed
78 79
}

80

unknown's avatar
unknown committed
81 82 83 84 85 86 87 88
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)))
    {
unknown's avatar
unknown committed
89
      uint length=name ? (uint) strlen(name) : 0;
unknown's avatar
unknown committed
90 91 92

      if ((entry=(host_entry*) malloc(sizeof(host_entry)+length+1)))
      {
unknown's avatar
unknown committed
93
	char *new_name;
unknown's avatar
unknown committed
94
	memcpy_fixed(&entry->ip, &in->s_addr, sizeof(in->s_addr));
unknown's avatar
unknown committed
95 96 97 98
	if (length)
	  memcpy(new_name= (char *) (entry+1), name, length+1);
	else
	  new_name=0;
unknown's avatar
unknown committed
99 100 101 102 103 104 105 106 107 108
	entry->hostname=new_name;
	entry->errors=0;
	(void) hostname_cache->add(entry);
      }
    }
    VOID(pthread_mutex_unlock(&hostname_cache->lock));
  }
}


unknown's avatar
unknown committed
109
inline void add_wrong_ip(struct in_addr *in)
unknown's avatar
unknown committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
{
  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));
}

132 133 134 135
/* Deal with systems that don't defined INADDR_LOOPBACK */
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK 0x7f000001UL
#endif
unknown's avatar
unknown committed
136 137 138

my_string ip_to_hostname(struct in_addr *in, uint *errors)
{
139
  uint i;
unknown's avatar
unknown committed
140 141
  host_entry *entry;
  DBUG_ENTER("ip_to_hostname");
142 143 144
  *errors=0;

  /* We always treat the loopback address as "localhost". */
145 146
  if (in->s_addr == htonl(INADDR_LOOPBACK))   // is expanded inline by gcc
    DBUG_RETURN((char *)my_localhost);
unknown's avatar
unknown committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

  /* Check first if we have name in cache */
  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));
187 188 189 190 191 192
    /*
      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.
    */
193
    if (tmp_errno == HOST_NOT_FOUND || tmp_errno == NO_DATA)
194
      add_wrong_ip(in);
unknown's avatar
unknown committed
195
    my_gethostbyname_r_free();
unknown's avatar
unknown committed
196 197 198 199 200 201
    DBUG_RETURN(0);
  }
  if (!hp->h_name[0])
  {
    DBUG_PRINT("error",("Got an empty hostname"));
    add_wrong_ip(in);
unknown's avatar
unknown committed
202
    my_gethostbyname_r_free();
unknown's avatar
unknown committed
203 204 205
    DBUG_RETURN(0);				// Don't allow empty hostnames
  }
  if (!(name=my_strdup(hp->h_name,MYF(0))))
unknown's avatar
unknown committed
206 207
  {
    my_gethostbyname_r_free();
unknown's avatar
unknown committed
208
    DBUG_RETURN(0);				// out of memory
unknown's avatar
unknown committed
209 210
  }
  my_gethostbyname_r_free();
unknown's avatar
unknown committed
211
#else
unknown's avatar
unknown committed
212
  VOID(pthread_mutex_lock(&LOCK_hostname));
unknown's avatar
unknown committed
213 214
  if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET)))
  {
unknown's avatar
unknown committed
215
    VOID(pthread_mutex_unlock(&LOCK_hostname));
216
    DBUG_PRINT("error",("gethostbyaddr returned %d",errno));
unknown's avatar
unknown committed
217 218

    if (errno == HOST_NOT_FOUND || errno == NO_DATA)
unknown's avatar
unknown committed
219 220
      goto add_wrong_ip_and_return;
    /* Failure, don't cache responce */
unknown's avatar
unknown committed
221
    DBUG_RETURN(0);
unknown's avatar
unknown committed
222
  }
223
  if (!hp->h_name[0])				// Don't allow empty hostnames
unknown's avatar
unknown committed
224
  {
unknown's avatar
unknown committed
225
    VOID(pthread_mutex_unlock(&LOCK_hostname));
unknown's avatar
unknown committed
226
    DBUG_PRINT("error",("Got an empty hostname"));
unknown's avatar
unknown committed
227
    goto add_wrong_ip_and_return;
unknown's avatar
unknown committed
228 229
  }
  if (!(name=my_strdup(hp->h_name,MYF(0))))
230
  {
unknown's avatar
unknown committed
231
    VOID(pthread_mutex_unlock(&LOCK_hostname));
unknown's avatar
unknown committed
232
    DBUG_RETURN(0);				// out of memory
233
  }
unknown's avatar
unknown committed
234
  check=gethostbyname(name);
unknown's avatar
unknown committed
235
  VOID(pthread_mutex_unlock(&LOCK_hostname));
unknown's avatar
unknown committed
236 237 238 239 240 241 242 243 244 245
  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 */
unknown's avatar
unknown committed
246
  if (my_isdigit(&my_charset_latin1,name[0]))
unknown's avatar
unknown committed
247 248
  {
    char *pos;
unknown's avatar
unknown committed
249
    for (pos= name+1 ; my_isdigit(&my_charset_latin1,*pos); pos++) ;
unknown's avatar
unknown committed
250 251 252 253
    if (*pos == '.')
    {
      DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'"));
      my_free(name,MYF(0));
unknown's avatar
unknown committed
254
      goto add_wrong_ip_and_return;
unknown's avatar
unknown committed
255 256 257 258
    }
  }

  /* Check that 'gethostbyname' returned the used ip */
259
  for (i=0; check->h_addr_list[i]; i++)
unknown's avatar
unknown committed
260 261 262 263 264 265 266 267 268
  {
    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));
269

unknown's avatar
unknown committed
270
add_wrong_ip_and_return:
unknown's avatar
unknown committed
271 272 273
  add_wrong_ip(in);
  DBUG_RETURN(0);
}