diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index 2fcba58231576..be57b93f44efa 100644 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -123,7 +123,7 @@ TARGET_LINK_LIBRARIES(resolveip mysys) IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") INCLUDE(CheckFunctionExists) INCLUDE(CheckLibraryExists) - MY_SEARCH_LIBS(inet_aton "nsl;socket;resolv" SOLARIS_NSL) + MY_SEARCH_LIBS(getaddrinfo "nsl;socket;resolv" SOLARIS_NSL) TARGET_LINK_LIBRARIES(resolveip ${SOLARIS_NSL}) ENDIF() ENDIF() diff --git a/extra/resolveip.c b/extra/resolveip.c index d964802d3b32a..c72ed696b8413 100644 --- a/extra/resolveip.c +++ b/extra/resolveip.c @@ -16,7 +16,7 @@ /* Resolves IP's to hostname and hostnames to IP's */ -#define VER "2.3" +#define VER "2.4" #include #include @@ -33,9 +33,6 @@ #include #include -#if !defined(_AIX) && !defined(h_errno) -extern int h_errno; -#endif static my_bool silent; @@ -104,9 +101,7 @@ static int get_options(int *argc,char ***argv) int main(int argc, char **argv) { - struct hostent *hpaddr; - in_addr_t taddr; - char *ip,**q; + char *ip; int error=0; MY_INIT(argv[0]); @@ -119,92 +114,135 @@ int main(int argc, char **argv) while (argc--) { - my_bool do_more; -#ifndef WIN32 - struct in_addr addr; -#endif - ip = *argv++; - - /* Not compatible with IPv6! Probably should use getnameinfo(). */ -#ifdef WIN32 - taddr = inet_addr(ip); - do_more= (taddr != INADDR_NONE); -#else - if ((do_more= (inet_aton(ip, &addr) != 0))) - taddr= addr.s_addr; -#endif - if (do_more) + struct in_addr addr4; + struct in6_addr addr6; + int is_ipv4, is_ipv6; + + ip = *argv++; + + is_ipv4= (inet_pton(AF_INET, ip, &addr4) == 1); + is_ipv6= (!is_ipv4 && inet_pton(AF_INET6, ip, &addr6) == 1); + + if (is_ipv4 || is_ipv6) { - if (taddr == htonl(INADDR_BROADCAST)) - { - puts("Broadcast"); - continue; + /* Reverse lookup: IP address -> hostname */ + + if (is_ipv4) + { + in_addr_t taddr= addr4.s_addr; + if (taddr == htonl(INADDR_BROADCAST)) + { + puts("Broadcast"); + continue; + } + if (taddr == htonl(INADDR_ANY)) + { + if (!taddr) + puts("Null-IP-Addr"); + else + puts("Old-Bcast"); + continue; + } } - if (taddr == htonl(INADDR_ANY)) + else if (IN6_IS_ADDR_UNSPECIFIED(&addr6)) { - if (!taddr) - puts("Null-IP-Addr"); - else - puts("Old-Bcast"); + puts("Null-IP-Addr"); continue; } - hpaddr = gethostbyaddr((char*) &(taddr), sizeof(struct in_addr),AF_INET); - if (hpaddr) { - if (silent) - puts(hpaddr->h_name); + struct sockaddr_storage sa; + socklen_t sa_len; + char hostname[NI_MAXHOST]; + int err; + + memset(&sa, 0, sizeof(sa)); + + if (is_ipv4) + { + struct sockaddr_in *sa4= (struct sockaddr_in *) &sa; + sa4->sin_family= AF_INET; + sa4->sin_addr= addr4; + sa_len= sizeof(struct sockaddr_in); + } else { - printf ("Host name of %s is %s", ip,hpaddr->h_name); - for (q = hpaddr->h_aliases; *q != 0; q++) - (void) printf(", %s", *q); - puts(""); + struct sockaddr_in6 *sa6= (struct sockaddr_in6 *) &sa; + sa6->sin6_family= AF_INET6; + sa6->sin6_addr= addr6; + sa_len= sizeof(struct sockaddr_in6); + } + + err= getnameinfo((struct sockaddr *) &sa, sa_len, + hostname, sizeof(hostname), NULL, 0, NI_NAMEREQD); + if (err == 0) + { + if (silent) + puts(hostname); + else + printf("Host name of %s is %s\n", ip, hostname); + } + else + { + error= 2; + fflush(stdout); + fprintf(stderr, "%s: Unable to find hostname for '%s'\n", + my_progname, ip); } - } - else - { - error=2; - fprintf(stderr,"%s: Unable to find hostname for '%s'\n",my_progname, - ip); } } else { - hpaddr = gethostbyname(ip); - if (!hpaddr) + /* Forward lookup: hostname -> IP address(es) */ + + struct addrinfo hints, *res, *rp; + int err; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family= AF_UNSPEC; + hints.ai_socktype= SOCK_STREAM; + + err= getaddrinfo(ip, NULL, &hints, &res); + if (err != 0) { - const char *err; - fprintf(stderr,"%s: Unable to find hostid for '%s'",my_progname,ip); - switch (h_errno) { - case HOST_NOT_FOUND: err="host not found"; break; - case TRY_AGAIN: err="try again"; break; - case NO_RECOVERY: err="no recovery"; break; - case NO_DATA: err="no_data"; break; - default: err=0; - } - if (err) - fprintf(stderr,": %s\n",err); - else - fprintf(stderr,"\n"); - error=2; + fflush(stdout); + fprintf(stderr, "%s: Unable to find hostid for '%s': %s\n", + my_progname, ip, gai_strerror(err)); + error= 2; } else if (silent) { - struct in_addr in; - memcpy((char*) &in.s_addr, (char*) *hpaddr->h_addr_list, - sizeof (in.s_addr)); - puts(inet_ntoa(in)); + char addr_str[INET6_ADDRSTRLEN]; + const void *addr_ptr; + + if (res->ai_family == AF_INET) + addr_ptr= &((struct sockaddr_in *) res->ai_addr)->sin_addr; + else + addr_ptr= &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; + + if (inet_ntop(res->ai_family, addr_ptr, addr_str, sizeof(addr_str))) + puts(addr_str); + + freeaddrinfo(res); } else { - char **p; - for (p = hpaddr->h_addr_list; *p != 0; p++) + for (rp= res; rp != NULL; rp= rp->ai_next) { - struct in_addr in; - memcpy(&in.s_addr, *p, sizeof (in.s_addr)); - printf ("IP address of %s is %s\n",ip,inet_ntoa(in)); + char addr_str[INET6_ADDRSTRLEN]; + const void *addr_ptr; + + if (rp->ai_family == AF_INET) + addr_ptr= &((struct sockaddr_in *) rp->ai_addr)->sin_addr; + else if (rp->ai_family == AF_INET6) + addr_ptr= &((struct sockaddr_in6 *) rp->ai_addr)->sin6_addr; + else + continue; + + if (inet_ntop(rp->ai_family, addr_ptr, addr_str, sizeof(addr_str))) + printf("IP address of %s is %s\n", ip, addr_str); } + freeaddrinfo(res); } } } diff --git a/mysql-test/main/resolveip.result b/mysql-test/main/resolveip.result new file mode 100644 index 0000000000000..317bbc67528af --- /dev/null +++ b/mysql-test/main/resolveip.result @@ -0,0 +1,67 @@ +# +# MDEV-39169: resolveip IPv6 support +# +# +# IPv4 special addresses (deterministic, no DNS needed) +# +Broadcast +Null-IP-Addr +# +# IPv4 reverse lookup +# +Host name of 127.0.0.1 is HOSTNAME +# +# IPv4 reverse lookup (silent mode) +# +HOSTNAME +# +# IPv6 special address (deterministic, no DNS needed) +# +Null-IP-Addr +# +# IPv6 unspecified address (full form) +# +Null-IP-Addr +# +# IPv6 reverse lookup +# +Host name of ::1 is HOSTNAME +# +# IPv6 reverse lookup (silent mode) +# +HOSTNAME +# +# IPv6 IPv4-mapped address +# +Host name of ::ffff:127.0.0.1 is HOSTNAME +# +# IPv6 full form address +# +Host name of 0000:0000:0000:0000:0000:0000:0000:0001 is HOSTNAME +# +# Multiple addresses in one call +# +Broadcast +Null-IP-Addr +# +# Mixed IPv4 and IPv6 addresses +# +Broadcast +Null-IP-Addr +# +# Mixed IP address and hostname +# +REPLACED +REPLACED +# +# Invalid hostname +# +resolveip: Unable to find hostid for 'nonexistent.invalid': NAME_RESOLUTION_ERROR +# +# Invalid address mixed with valid address +# +Broadcast +resolveip: Unable to find hostid for 'nonexistent.invalid': NAME_RESOLUTION_ERROR +# +# End of 13.0 tests +# diff --git a/mysql-test/main/resolveip.test b/mysql-test/main/resolveip.test new file mode 100644 index 0000000000000..874ffefbc28b6 --- /dev/null +++ b/mysql-test/main/resolveip.test @@ -0,0 +1,88 @@ +--source include/not_windows.inc +--source include/check_ipv6.inc + +# Reverse DNS tests (getnameinfo with NI_NAMEREQD) require: +# - /etc/hosts entries for 127.0.0.1 and ::1 (or working DNS PTR records) +# - "hosts: files ..." in /etc/nsswitch.conf (or equivalent resolver config) +# - glibc nss_files backend mapping ::ffff:127.0.0.1 to the 127.0.0.1 entry +# - - musl libc (Alpine) may behave differently from glibc for mapped addresses +# If reverse lookups fail, check: getent hosts 127.0.0.1 ::1 ::ffff:127.0.0.1 + +--echo # +--echo # MDEV-39169: resolveip IPv6 support +--echo # + +--echo # +--echo # IPv4 special addresses (deterministic, no DNS needed) +--echo # +--exec $MYSQL_RESOLVEIP 255.255.255.255 +--exec $MYSQL_RESOLVEIP 0.0.0.0 + +--echo # +--echo # IPv4 reverse lookup +--echo # +--exec $MYSQL_RESOLVEIP 127.0.0.1 2>&1 | sed 's/Host name of 127\.0\.0\.1 is .*/Host name of 127.0.0.1 is HOSTNAME/' + +--echo # +--echo # IPv4 reverse lookup (silent mode) +--echo # +--exec $MYSQL_RESOLVEIP -s 127.0.0.1 2>&1 | sed 's/.*/HOSTNAME/' + +--echo # +--echo # IPv6 special address (deterministic, no DNS needed) +--echo # +--exec $MYSQL_RESOLVEIP :: + +--echo # +--echo # IPv6 unspecified address (full form) +--echo # +--exec $MYSQL_RESOLVEIP 0000:0000:0000:0000:0000:0000:0000:0000 + +--echo # +--echo # IPv6 reverse lookup +--echo # +--exec $MYSQL_RESOLVEIP ::1 2>&1 | sed 's/Host name of ::1 is .*/Host name of ::1 is HOSTNAME/' + +--echo # +--echo # IPv6 reverse lookup (silent mode) +--echo # +--exec $MYSQL_RESOLVEIP -s ::1 2>&1 | sed 's/.*/HOSTNAME/' + +--echo # +--echo # IPv6 IPv4-mapped address +--echo # +--exec $MYSQL_RESOLVEIP ::ffff:127.0.0.1 2>&1 | sed 's/Host name of ::ffff:127\.0\.0\.1 is .*/Host name of ::ffff:127.0.0.1 is HOSTNAME/' + +--echo # +--echo # IPv6 full form address +--echo # +--exec $MYSQL_RESOLVEIP 0000:0000:0000:0000:0000:0000:0000:0001 2>&1 | sed 's/Host name of 0000:0000:0000:0000:0000:0000:0000:0001 is .*/Host name of 0000:0000:0000:0000:0000:0000:0000:0001 is HOSTNAME/' + +--echo # +--echo # Multiple addresses in one call +--echo # +--exec $MYSQL_RESOLVEIP 255.255.255.255 0.0.0.0 + +--echo # +--echo # Mixed IPv4 and IPv6 addresses +--echo # +--exec $MYSQL_RESOLVEIP 255.255.255.255 :: + +--echo # +--echo # Mixed IP address and hostname +--echo # +--exec $MYSQL_RESOLVEIP -s 127.0.0.1 localhost | sed 's/.*/REPLACED/' + +--echo # +--echo # Invalid hostname +--echo # +--exec $MYSQL_RESOLVEIP nonexistent.invalid 2>&1 | sed "s|.*Unable to find hostid for 'nonexistent.invalid': .*|resolveip: Unable to find hostid for 'nonexistent.invalid': NAME_RESOLUTION_ERROR|" + +--echo # +--echo # Invalid address mixed with valid address +--echo # +--exec $MYSQL_RESOLVEIP 255.255.255.255 nonexistent.invalid 2>&1 | sed "s|.*Unable to find hostid for 'nonexistent.invalid': .*|resolveip: Unable to find hostid for 'nonexistent.invalid': NAME_RESOLUTION_ERROR|" + +--echo # +--echo # End of 13.0 tests +--echo # diff --git a/mysql-test/mariadb-test-run.pl b/mysql-test/mariadb-test-run.pl index 788927069015e..6762086571f55 100755 --- a/mysql-test/mariadb-test-run.pl +++ b/mysql-test/mariadb-test-run.pl @@ -2294,6 +2294,16 @@ sub environment_setup { "$path_client_bindir/mariadb-migrate-config-file"); $ENV{'MARIADB_MIGRATE_CONFIG_FILE'}= native_path($exe_mariadb_migrate_config_file) if $exe_mariadb_migrate_config_file; + # ---------------------------------------------------- + # resolveip (not built on Windows) + # ---------------------------------------------------- + unless (IS_WINDOWS) { + my $exe_resolveip= + mtr_exe_exists("$bindir/extra$multiconfig/resolveip", + "$path_client_bindir/resolveip"); + $ENV{'MYSQL_RESOLVEIP'}= native_path($exe_resolveip); + } + # ---------------------------------------------------- # myisam tools # ----------------------------------------------------