1 /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA */
15 
16 
17 /**
18   @file
19 
20   @brief
21   Get hostname for an IP address.
22 
23   Hostnames are checked with reverse name lookup and checked that they
24   doesn't resemble an IP address.
25 */
26 
27 #include "sql_priv.h"
28 #include "hostname.h"
29 #include "my_global.h"
30 #ifndef __WIN__
31 #include <netdb.h>        // getservbyname, servent
32 #endif
33 #include "hash_filo.h"
34 #include <m_ctype.h>
35 #include "log.h"                                // sql_print_warning,
36                                                 // sql_print_information
37 #include "violite.h"                            // vio_getnameinfo,
38                                                 // vio_get_normalized_ip_string
39 #ifdef	__cplusplus
40 extern "C" {					// Because of SCO 3.2V4.2
41 #endif
42 #if !defined( __WIN__)
43 #ifdef HAVE_SYS_UN_H
44 #include <sys/un.h>
45 #endif
46 #include <sys/utsname.h>
47 #endif // __WIN__
48 #ifdef	__cplusplus
49 }
50 #endif
51 
52 /*
53   HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache.
54 */
55 
56 #define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
57 
58 /**
59   An entry in the hostname hash table cache.
60 
61   Host name cache does two things:
62     - caches host names to save DNS look ups;
63     - counts connect errors from IP.
64 
65   Host name can be NULL (that means DNS look up failed), but connect errors
66   still are counted.
67 */
68 
69 class Host_entry :public hash_filo_element
70 {
71 public:
72   /**
73     Client IP address. This is the key used with the hash table.
74 
75     The client IP address is always expressed in IPv6, even when the
76     network IPv6 stack is not present.
77 
78     This IP address is never used to connect to a socket.
79   */
80   char ip_key[HOST_ENTRY_KEY_SIZE];
81 
82   /**
83     Number of errors during handshake phase from the IP address.
84   */
85   uint connect_errors;
86 
87   /**
88     One of the host names for the IP address. May be NULL.
89   */
90   const char *hostname;
91 };
92 
93 static hash_filo *hostname_cache;
94 
hostname_cache_refresh()95 void hostname_cache_refresh()
96 {
97   hostname_cache->clear();
98 }
99 
hostname_cache_init()100 bool hostname_cache_init()
101 {
102   Host_entry tmp;
103   uint key_offset= (uint) ((char*) (&tmp.ip_key) - (char*) &tmp);
104 
105   if (!(hostname_cache= new hash_filo(HOST_CACHE_SIZE,
106                                       key_offset, HOST_ENTRY_KEY_SIZE,
107                                       NULL, (my_hash_free_key) free,
108                                       &my_charset_bin)))
109     return 1;
110 
111   hostname_cache->clear();
112 
113   return 0;
114 }
115 
hostname_cache_free()116 void hostname_cache_free()
117 {
118   delete hostname_cache;
119   hostname_cache= NULL;
120 }
121 
prepare_hostname_cache_key(const char * ip_string,char * ip_key)122 static void prepare_hostname_cache_key(const char *ip_string,
123                                        char *ip_key)
124 {
125   int ip_string_length= strlen(ip_string);
126   DBUG_ASSERT(ip_string_length < HOST_ENTRY_KEY_SIZE);
127 
128   memset(ip_key, 0, HOST_ENTRY_KEY_SIZE);
129   memcpy(ip_key, ip_string, ip_string_length);
130 }
131 
hostname_cache_search(const char * ip_key)132 static inline Host_entry *hostname_cache_search(const char *ip_key)
133 {
134   return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
135 }
136 
add_hostname_impl(const char * ip_key,const char * hostname)137 static bool add_hostname_impl(const char *ip_key, const char *hostname)
138 {
139   if (hostname_cache_search(ip_key))
140     return FALSE;
141 
142   size_t hostname_size= hostname ? strlen(hostname) + 1 : 0;
143 
144   Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) + hostname_size);
145 
146   if (!entry)
147     return TRUE;
148 
149   char *hostname_copy;
150 
151   memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
152 
153   if (hostname_size)
154   {
155     hostname_copy= (char *) (entry + 1);
156     memcpy(hostname_copy, hostname, hostname_size);
157 
158     DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
159                         (const char *) ip_key,
160                         (const char *) hostname_copy));
161   }
162   else
163   {
164     hostname_copy= NULL;
165 
166     DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
167                         (const char *) ip_key));
168   }
169 
170   entry->hostname= hostname_copy;
171   entry->connect_errors= 0;
172 
173   return hostname_cache->add(entry);
174 }
175 
add_hostname(const char * ip_key,const char * hostname)176 static bool add_hostname(const char *ip_key, const char *hostname)
177 {
178   if (specialflag & SPECIAL_NO_HOST_CACHE)
179     return FALSE;
180 
181   mysql_mutex_lock(&hostname_cache->lock);
182 
183   bool err_status= add_hostname_impl(ip_key, hostname);
184 
185   mysql_mutex_unlock(&hostname_cache->lock);
186 
187   return err_status;
188 }
189 
inc_host_errors(const char * ip_string)190 void inc_host_errors(const char *ip_string)
191 {
192   if (!ip_string)
193     return;
194 
195   char ip_key[HOST_ENTRY_KEY_SIZE];
196   prepare_hostname_cache_key(ip_string, ip_key);
197 
198   mysql_mutex_lock(&hostname_cache->lock);
199 
200   Host_entry *entry= hostname_cache_search(ip_key);
201 
202   if (entry)
203     entry->connect_errors++;
204 
205   mysql_mutex_unlock(&hostname_cache->lock);
206 }
207 
208 
reset_host_errors(const char * ip_string)209 void reset_host_errors(const char *ip_string)
210 {
211   if (!ip_string)
212     return;
213 
214   char ip_key[HOST_ENTRY_KEY_SIZE];
215   prepare_hostname_cache_key(ip_string, ip_key);
216 
217   mysql_mutex_lock(&hostname_cache->lock);
218 
219   Host_entry *entry= hostname_cache_search(ip_key);
220 
221   if (entry)
222     entry->connect_errors= 0;
223 
224   mysql_mutex_unlock(&hostname_cache->lock);
225 }
226 
227 
is_ip_loopback(const struct sockaddr * ip)228 static inline bool is_ip_loopback(const struct sockaddr *ip)
229 {
230   switch (ip->sa_family) {
231   case AF_INET:
232     {
233       /* Check for IPv4 127.0.0.1. */
234       struct in_addr *ip4= &((struct sockaddr_in *) ip)->sin_addr;
235       return ntohl(ip4->s_addr) == INADDR_LOOPBACK;
236     }
237 
238 #ifdef HAVE_IPV6
239   case AF_INET6:
240     {
241       /* Check for IPv6 ::1. */
242       struct in6_addr *ip6= &((struct sockaddr_in6 *) ip)->sin6_addr;
243       return IN6_IS_ADDR_LOOPBACK(ip6);
244     }
245 #endif /* HAVE_IPV6 */
246 
247   default:
248     return FALSE;
249   }
250 }
251 
is_hostname_valid(const char * hostname)252 static inline bool is_hostname_valid(const char *hostname)
253 {
254   /*
255     A hostname is invalid if it starts with a number followed by a dot
256     (IPv4 address).
257   */
258 
259   if (!my_isdigit(&my_charset_latin1, hostname[0]))
260     return TRUE;
261 
262   const char *p= hostname + 1;
263 
264   while (my_isdigit(&my_charset_latin1, *p))
265     ++p;
266 
267   return *p != '.';
268 }
269 
270 /**
271   Resolve IP-address to host name.
272 
273   This function does the following things:
274     - resolves IP-address;
275     - employs Forward Confirmed Reverse DNS technique to validate IP-address;
276     - returns host name if IP-address is validated;
277     - set value to out-variable connect_errors -- this variable represents the
278       number of connection errors from the specified IP-address.
279 
280   NOTE: connect_errors are counted (are supported) only for the clients
281   where IP-address can be resolved and FCrDNS check is passed.
282 
283   @param [in]  ip_storage IP address (sockaddr). Must be set.
284   @param [in]  ip_string  IP address (string). Must be set.
285   @param [out] hostname
286   @param [out] connect_errors
287 
288   @return Error status
289   @retval FALSE Success
290   @retval TRUE Error
291 
292   The function does not set/report MySQL server error in case of failure.
293   It's caller's responsibility to handle failures of this function
294   properly.
295 */
296 
ip_to_hostname(struct sockaddr_storage * ip_storage,const char * ip_string,char ** hostname,uint * connect_errors)297 bool ip_to_hostname(struct sockaddr_storage *ip_storage,
298                     const char *ip_string,
299                     char **hostname, uint *connect_errors)
300 {
301   const struct sockaddr *ip= (const sockaddr *) ip_storage;
302   int err_code;
303   bool err_status;
304 
305   DBUG_ENTER("ip_to_hostname");
306   DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
307                       (const char *) ip_string,
308                       (int) ip->sa_family));
309 
310   /* Check if we have loopback address (127.0.0.1 or ::1). */
311 
312   if (is_ip_loopback(ip))
313   {
314     DBUG_PRINT("info", ("Loopback address detected."));
315 
316     *connect_errors= 0; /* Do not count connect errors from localhost. */
317     *hostname= (char *) my_localhost;
318 
319     DBUG_RETURN(FALSE);
320   }
321 
322   /* Prepare host name cache key. */
323 
324   char ip_key[HOST_ENTRY_KEY_SIZE];
325   prepare_hostname_cache_key(ip_string, ip_key);
326 
327   /* Check first if we have host name in the cache. */
328 
329   if (!(specialflag & SPECIAL_NO_HOST_CACHE))
330   {
331     mysql_mutex_lock(&hostname_cache->lock);
332 
333     Host_entry *entry= hostname_cache_search(ip_key);
334 
335     if (entry)
336     {
337       *connect_errors= entry->connect_errors;
338       *hostname= NULL;
339 
340       if (entry->hostname)
341         *hostname= my_strdup(entry->hostname, MYF(0));
342 
343       DBUG_PRINT("info",("IP (%s) has been found in the cache. "
344                          "Hostname: '%s'; connect_errors: %d",
345                          (const char *) ip_key,
346                          (const char *) (*hostname? *hostname : "null"),
347                          (int) *connect_errors));
348 
349       mysql_mutex_unlock(&hostname_cache->lock);
350 
351       DBUG_RETURN(FALSE);
352     }
353 
354     mysql_mutex_unlock(&hostname_cache->lock);
355   }
356 
357   /*
358     Resolve host name. Return an error if a host name can not be resolved
359     (instead of returning the numeric form of the host name).
360   */
361 
362   char hostname_buffer[NI_MAXHOST];
363 
364   DBUG_PRINT("info", ("Resolving '%s'...", (const char *) ip_key));
365 
366   err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0,
367                             NI_NAMEREQD);
368 
369   /* BEGIN : DEBUG */
370   DBUG_EXECUTE_IF("addr_fake_ipv4",
371                   {
372                     strcpy(hostname_buffer, "santa.claus.ipv4.example.com");
373                     err_code= 0;
374                   };);
375   /* END   : DEBUG */
376 
377   if (err_code)
378   {
379     // NOTE: gai_strerror() returns a string ending by a dot.
380 
381     DBUG_PRINT("error", ("IP address '%s' could not be resolved: %s",
382                          (const char *) ip_key,
383                          (const char *) gai_strerror(err_code)));
384 
385     sql_print_warning("IP address '%s' could not be resolved: %s",
386                       (const char *) ip_key,
387                       (const char *) gai_strerror(err_code));
388 
389     if (vio_is_no_name_error(err_code))
390     {
391       /*
392         The no-name error means that there is no reverse address mapping
393         for the IP address. A host name can not be resolved.
394 
395         If it is not the no-name error, we should not cache the hostname
396         (or rather its absence), because the failure might be transient.
397       */
398 
399       add_hostname(ip_key, NULL);
400 
401       *hostname= NULL;
402       *connect_errors= 0; /* New IP added to the cache. */
403     }
404 
405     DBUG_RETURN(FALSE);
406   }
407 
408   DBUG_PRINT("info", ("IP '%s' resolved to '%s'.",
409                       (const char *) ip_key,
410                       (const char *) hostname_buffer));
411 
412   /*
413     Validate hostname: the server does not accept host names, which
414     resemble IP addresses.
415 
416     The thing is that theoretically, a host name can be in a form of IPv4
417     address (123.example.org, or 1.2 or even 1.2.3.4). We have to deny such
418     host names because ACL-systems is not designed to work with them.
419 
420     For example, it is possible to specify a host name mask (like
421     192.168.1.%) for an ACL rule. Then, if IPv4-like hostnames are allowed,
422     there is a security hole: instead of allowing access for
423     192.168.1.0/255 network (which was assumed by the user), the access
424     will be allowed for host names like 192.168.1.example.org.
425   */
426 
427   if (!is_hostname_valid(hostname_buffer))
428   {
429     DBUG_PRINT("error", ("IP address '%s' has been resolved "
430                          "to the host name '%s', which resembles "
431                          "IPv4-address itself.",
432                          (const char *) ip_key,
433                          (const char *) hostname_buffer));
434 
435     sql_print_warning("IP address '%s' has been resolved "
436                       "to the host name '%s', which resembles "
437                       "IPv4-address itself.",
438                       (const char *) ip_key,
439                       (const char *) hostname_buffer);
440 
441     err_status= add_hostname(ip_key, NULL);
442 
443     *hostname= NULL;
444     *connect_errors= 0; /* New IP added to the cache. */
445 
446     DBUG_RETURN(err_status);
447   }
448 
449   /*
450     To avoid crashing the server in DBUG_EXECUTE_IF,
451     Define a variable which depicts state of addr_info_list.
452   */
453   bool free_addr_info_list= false;
454 
455   /* Get IP-addresses for the resolved host name (FCrDNS technique). */
456 
457   struct addrinfo hints;
458   struct addrinfo *addr_info_list;
459 
460   memset(&hints, 0, sizeof (struct addrinfo));
461   hints.ai_flags= AI_PASSIVE;
462   hints.ai_socktype= SOCK_STREAM;
463   hints.ai_family= AF_UNSPEC;
464 
465   DBUG_PRINT("info", ("Getting IP addresses for hostname '%s'...",
466                       (const char *) hostname_buffer));
467 
468   err_code= getaddrinfo(hostname_buffer, NULL, &hints, &addr_info_list);
469   if (err_code == 0)
470     free_addr_info_list= true;
471 
472   /* BEGIN : DEBUG */
473   DBUG_EXECUTE_IF("addr_fake_ipv4",
474                   {
475                     if (free_addr_info_list)
476                       freeaddrinfo(addr_info_list);
477 
478                     struct sockaddr_in *debug_addr;
479                     static struct sockaddr_in debug_sock_addr[2];
480                     static struct addrinfo debug_addr_info[2];
481                     /* Simulating ipv4 192.0.2.5 */
482                     debug_addr= & debug_sock_addr[0];
483                     debug_addr->sin_family= AF_INET;
484                     debug_addr->sin_addr.s_addr= inet_addr("192.0.2.5");
485 
486                     /* Simulating ipv4 192.0.2.4 */
487                     debug_addr= & debug_sock_addr[1];
488                     debug_addr->sin_family= AF_INET;
489                     debug_addr->sin_addr.s_addr= inet_addr("192.0.2.4");
490 
491                     debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
492                     debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in);
493                     debug_addr_info[0].ai_next= & debug_addr_info[1];
494 
495                     debug_addr_info[1].ai_addr= (struct sockaddr*) & debug_sock_addr[1];
496                     debug_addr_info[1].ai_addrlen= sizeof (struct sockaddr_in);
497                     debug_addr_info[1].ai_next= NULL;
498 
499                     addr_info_list= & debug_addr_info[0];
500                     err_code= 0;
501                     free_addr_info_list= false;
502                   };);
503 
504   /* END   : DEBUG */
505 
506   if (err_code == EAI_NONAME)
507   {
508     /*
509       Don't cache responses when the DNS server is down, as otherwise
510       transient DNS failure may leave any number of clients (those
511       that attempted to connect during the outage) unable to connect
512       indefinitely.
513     */
514 
515     err_status= add_hostname(ip_key, NULL);
516 
517     *hostname= NULL;
518     *connect_errors= 0; /* New IP added to the cache. */
519 
520     DBUG_RETURN(err_status);
521   }
522   else if (err_code)
523   {
524     DBUG_PRINT("error", ("getaddrinfo() failed with error code %d.", err_code));
525     DBUG_RETURN(TRUE);
526   }
527 
528   /* Check that getaddrinfo() returned the used IP (FCrDNS technique). */
529 
530   DBUG_PRINT("info", ("The following IP addresses found for '%s':",
531                       (const char *) hostname_buffer));
532 
533   for (struct addrinfo *addr_info= addr_info_list;
534        addr_info; addr_info= addr_info->ai_next)
535   {
536     char ip_buffer[HOST_ENTRY_KEY_SIZE];
537 
538     {
539       err_status=
540         vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen,
541                                      ip_buffer, sizeof (ip_buffer));
542       DBUG_ASSERT(!err_status);
543     }
544 
545     DBUG_PRINT("info", ("  - '%s'", (const char *) ip_buffer));
546 
547     if (strcmp(ip_key, ip_buffer) == 0)
548     {
549       /* Copy host name string to be stored in the cache. */
550 
551       *hostname= my_strdup(hostname_buffer, MYF(0));
552 
553       if (!*hostname)
554       {
555         DBUG_PRINT("error", ("Out of memory."));
556 
557         if (free_addr_info_list)
558           freeaddrinfo(addr_info_list);
559         DBUG_RETURN(TRUE);
560       }
561 
562       break;
563     }
564   }
565 
566   /* Log resolved IP-addresses if no match was found. */
567 
568   if (!*hostname)
569   {
570     sql_print_information("Hostname '%s' does not resolve to '%s'.",
571                           (const char *) hostname_buffer,
572                           (const char *) ip_key);
573     sql_print_information("Hostname '%s' has the following IP addresses:",
574                           (const char *) hostname_buffer);
575 
576     for (struct addrinfo *addr_info= addr_info_list;
577          addr_info; addr_info= addr_info->ai_next)
578     {
579       char ip_buffer[HOST_ENTRY_KEY_SIZE];
580 
581       err_status=
582         vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen,
583                                      ip_buffer, sizeof (ip_buffer));
584       DBUG_ASSERT(!err_status);
585 
586       sql_print_information(" - %s\n", (const char *) ip_buffer);
587     }
588   }
589 
590   /* Free the result of getaddrinfo(). */
591 
592   if (free_addr_info_list)
593     freeaddrinfo(addr_info_list);
594 
595   /* Add an entry for the IP to the cache. */
596 
597   if (*hostname)
598   {
599     err_status= add_hostname(ip_key, *hostname);
600     *connect_errors= 0;
601   }
602   else
603   {
604     DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo()."));
605 
606     err_status= add_hostname(ip_key, NULL);
607     *hostname= NULL;
608     *connect_errors= 0;
609   }
610 
611   DBUG_RETURN(err_status);
612 }
613