hostname.cc 6.55 KB
Newer Older
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4 5 6
   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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   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
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
29
#if !defined( __WIN__) && !defined(OS2)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
30
#if !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
31
#include <sys/resource.h>
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
32
#endif /* __NETWARE__ */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
#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;
53
static pthread_mutex_t LOCK_hostname;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
54 55 56 57 58 59 60 61

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

bool hostname_cache_init()
{
62 63
  host_entry tmp;
  uint offset= (uint) ((char*) (&tmp.ip) - (char*) &tmp);
64
  (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW);
65 66

  if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
67
				     sizeof(struct in_addr),NULL,
68
				     (hash_free_key) free)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
69 70 71 72 73 74 75
    return 1;
  hostname_cache->clear();
  return 0;
}

void hostname_cache_free()
{
76
  (void) pthread_mutex_destroy(&LOCK_hostname);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
77 78 79 80 81 82 83 84 85 86 87
  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)))
    {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
88
      uint length=name ? (uint) strlen(name) : 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
89 90 91

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


monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
108
inline void add_wrong_ip(struct in_addr *in)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
{
  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)
{
134
  uint i;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
135 136 137 138 139 140 141 142 143 144 145 146 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
  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);
179
    my_gethostbyname_r_free();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
180 181 182 183 184 185
    DBUG_RETURN(0);
  }
  if (!hp->h_name[0])
  {
    DBUG_PRINT("error",("Got an empty hostname"));
    add_wrong_ip(in);
186
    my_gethostbyname_r_free();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
187 188 189
    DBUG_RETURN(0);				// Don't allow empty hostnames
  }
  if (!(name=my_strdup(hp->h_name,MYF(0))))
190 191
  {
    my_gethostbyname_r_free();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
192
    DBUG_RETURN(0);				// out of memory
193 194
  }
  my_gethostbyname_r_free();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
195
#else
196
  VOID(pthread_mutex_lock(&LOCK_hostname));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
197 198
  if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET)))
  {
199
    VOID(pthread_mutex_unlock(&LOCK_hostname));
200 201
    DBUG_PRINT("error",("gethostbyaddr returned %d",errno));
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
202
  }
203
  if (!hp->h_name[0])				// Don't allow empty hostnames
bk@work.mysql.com's avatar
bk@work.mysql.com committed
204
  {
205
    VOID(pthread_mutex_unlock(&LOCK_hostname));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
206
    DBUG_PRINT("error",("Got an empty hostname"));
207
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
208 209
  }
  if (!(name=my_strdup(hp->h_name,MYF(0))))
210
  {
211
    VOID(pthread_mutex_unlock(&LOCK_hostname));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
212
    DBUG_RETURN(0);				// out of memory
213
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
214
  check=gethostbyname(name);
215
  VOID(pthread_mutex_unlock(&LOCK_hostname));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
  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));
234
      goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
235 236 237 238
    }
  }

  /* Check that 'gethostbyname' returned the used ip */
239
  for (i=0; check->h_addr_list[i]; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
240 241 242 243 244 245 246 247 248
  {
    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));
249 250

err:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
251 252 253
  add_wrong_ip(in);
  DBUG_RETURN(0);
}