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