Unverified Commit 09e0d338 authored by Binbin's avatar Binbin Committed by GitHub
Browse files

redis-cli adds -4 / -6 options to determine IPV4 / IPV6 priority in DNS lookup (#11315)

This PR, we added -4 and -6 options to redis-cli to determine
IPV4 / IPV6 priority in DNS lookup.
This was mentioned in
https://github.com/redis/redis/pull/11151#issuecomment-1231570651



For now it's only used in CLUSTER MEET.

The options also made it possible to reliably test dns lookup in CI,
using this option, we can add some localhost tests for #11151.

The commit was cherry-picked from #11151, back then we decided to split
the PR.
Co-authored-by: default avatarViktor Söderqvist <viktor.soderqvist@est.tech>
parent 23e980e7
...@@ -239,7 +239,11 @@ int anetRecvTimeout(char *err, int fd, long long ms) { ...@@ -239,7 +239,11 @@ int anetRecvTimeout(char *err, int fd, long long ms) {
* *
* If flags is set to ANET_IP_ONLY the function only resolves hostnames * If flags is set to ANET_IP_ONLY the function only resolves hostnames
* that are actually already IPv4 or IPv6 addresses. This turns the function * that are actually already IPv4 or IPv6 addresses. This turns the function
* into a validating / normalizing function. */ * into a validating / normalizing function.
*
* If the flag ANET_PREFER_IPV4 is set, IPv4 is preferred over IPv6.
* If the flag ANET_PREFER_IPV6 is set, IPv6 is preferred over IPv4.
* */
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len, int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
int flags) int flags)
{ {
...@@ -249,9 +253,20 @@ int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len, ...@@ -249,9 +253,20 @@ int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
memset(&hints,0,sizeof(hints)); memset(&hints,0,sizeof(hints));
if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST; if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
if (flags & ANET_PREFER_IPV4 && !(flags & ANET_PREFER_IPV6)) {
hints.ai_family = AF_INET;
} else if (flags & ANET_PREFER_IPV6 && !(flags & ANET_PREFER_IPV4)) {
hints.ai_family = AF_INET6;
}
hints.ai_socktype = SOCK_STREAM; /* specify socktype to avoid dups */ hints.ai_socktype = SOCK_STREAM; /* specify socktype to avoid dups */
if ((rv = getaddrinfo(host, NULL, &hints, &info)) != 0) { rv = getaddrinfo(host, NULL, &hints, &info);
if (rv != 0 && hints.ai_family != AF_UNSPEC) {
/* Try the other IP version. */
hints.ai_family = (hints.ai_family == AF_INET) ? AF_INET6 : AF_INET;
rv = getaddrinfo(host, NULL, &hints, &info);
}
if (rv != 0) {
anetSetError(err, "%s", gai_strerror(rv)); anetSetError(err, "%s", gai_strerror(rv));
return ANET_ERR; return ANET_ERR;
} }
......
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
/* Flags used with certain functions. */ /* Flags used with certain functions. */
#define ANET_NONE 0 #define ANET_NONE 0
#define ANET_IP_ONLY (1<<0) #define ANET_IP_ONLY (1<<0)
#define ANET_PREFER_IPV4 (1<<1)
#define ANET_PREFER_IPV6 (1<<2)
#if defined(__sun) || defined(_AIX) #if defined(__sun) || defined(_AIX)
#define AF_LOCAL AF_UNIX #define AF_LOCAL AF_UNIX
......
...@@ -275,6 +275,8 @@ static struct config { ...@@ -275,6 +275,8 @@ static struct config {
char *server_version; char *server_version;
char *test_hint; char *test_hint;
char *test_hint_file; char *test_hint_file;
int prefer_ipv4; /* Prefer IPv4 over IPv6 on DNS lookup. */
int prefer_ipv6; /* Prefer IPv6 over IPv4 on DNS lookup. */
} config; } config;
/* User preferences. */ /* User preferences. */
...@@ -2768,6 +2770,10 @@ static int parseOptions(int argc, char **argv) { ...@@ -2768,6 +2770,10 @@ static int parseOptions(int argc, char **argv) {
config.set_errcode = 1; config.set_errcode = 1;
} else if (!strcmp(argv[i],"--verbose")) { } else if (!strcmp(argv[i],"--verbose")) {
config.verbose = 1; config.verbose = 1;
} else if (!strcmp(argv[i],"-4")) {
config.prefer_ipv4 = 1;
} else if (!strcmp(argv[i],"-6")) {
config.prefer_ipv6 = 1;
} else if (!strcmp(argv[i],"--cluster") && !lastarg) { } else if (!strcmp(argv[i],"--cluster") && !lastarg) {
if (CLUSTER_MANAGER_MODE()) usage(1); if (CLUSTER_MANAGER_MODE()) usage(1);
char *cmd = argv[++i]; char *cmd = argv[++i];
...@@ -2952,6 +2958,11 @@ static int parseOptions(int argc, char **argv) { ...@@ -2952,6 +2958,11 @@ static int parseOptions(int argc, char **argv) {
exit(1); exit(1);
} }
if (config.prefer_ipv4 && config.prefer_ipv6) {
fprintf(stderr, "Options -4 and -6 are mutually exclusive.\n");
exit(1);
}
return i; return i;
} }
...@@ -3028,6 +3039,8 @@ static void usage(int err) { ...@@ -3028,6 +3039,8 @@ static void usage(int err) {
" -D <delimiter> Delimiter between responses for raw formatting (default: \\n).\n" " -D <delimiter> Delimiter between responses for raw formatting (default: \\n).\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n" " -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
" -e Return exit error code when command execution fails.\n" " -e Return exit error code when command execution fails.\n"
" -4 Prefer IPv4 over IPv6 on DNS lookup.\n"
" -6 Prefer IPv6 over IPv4 on DNS lookup.\n"
"%s" "%s"
" --raw Use raw formatting for replies (default when STDOUT is\n" " --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty).\n" " not a tty).\n"
...@@ -7071,7 +7084,10 @@ assign_replicas: ...@@ -7071,7 +7084,10 @@ assign_replicas:
first = node; first = node;
/* Although hiredis supports connecting to a hostname, CLUSTER /* Although hiredis supports connecting to a hostname, CLUSTER
* MEET requires an IP address, so we do a DNS lookup here. */ * MEET requires an IP address, so we do a DNS lookup here. */
if (anetResolve(NULL, first->ip, first_ip, sizeof(first_ip), ANET_NONE) int anet_flags = ANET_NONE;
if (config.prefer_ipv4) anet_flags |= ANET_PREFER_IPV4;
if (config.prefer_ipv6) anet_flags |= ANET_PREFER_IPV6;
if (anetResolve(NULL, first->ip, first_ip, sizeof(first_ip), anet_flags)
== ANET_ERR) == ANET_ERR)
{ {
fprintf(stderr, "Invalid IP address or hostname specified: %s\n", first->ip); fprintf(stderr, "Invalid IP address or hostname specified: %s\n", first->ip);
...@@ -7266,7 +7282,10 @@ static int clusterManagerCommandAddNode(int argc, char **argv) { ...@@ -7266,7 +7282,10 @@ static int clusterManagerCommandAddNode(int argc, char **argv) {
"join the cluster.\n", ip, port); "join the cluster.\n", ip, port);
/* CLUSTER MEET requires an IP address, so we do a DNS lookup here. */ /* CLUSTER MEET requires an IP address, so we do a DNS lookup here. */
char first_ip[NET_IP_STR_LEN]; char first_ip[NET_IP_STR_LEN];
if (anetResolve(NULL, first->ip, first_ip, sizeof(first_ip), ANET_NONE) == ANET_ERR) { int anet_flags = ANET_NONE;
if (config.prefer_ipv4) anet_flags |= ANET_PREFER_IPV4;
if (config.prefer_ipv6) anet_flags |= ANET_PREFER_IPV6;
if (anetResolve(NULL, first->ip, first_ip, sizeof(first_ip), anet_flags) == ANET_ERR) {
fprintf(stderr, "Invalid IP address or hostname specified: %s\n", first->ip); fprintf(stderr, "Invalid IP address or hostname specified: %s\n", first->ip);
success = 0; success = 0;
goto cleanup; goto cleanup;
...@@ -9862,6 +9881,8 @@ int main(int argc, char **argv) { ...@@ -9862,6 +9881,8 @@ int main(int argc, char **argv) {
config.no_auth_warning = 0; config.no_auth_warning = 0;
config.in_multi = 0; config.in_multi = 0;
config.server_version = NULL; config.server_version = NULL;
config.prefer_ipv4 = 0;
config.prefer_ipv6 = 0;
config.cluster_manager_command.name = NULL; config.cluster_manager_command.name = NULL;
config.cluster_manager_command.argc = 0; config.cluster_manager_command.argc = 0;
config.cluster_manager_command.argv = NULL; config.cluster_manager_command.argv = NULL;
......
...@@ -327,6 +327,8 @@ test {Migrate the last slot away from a node using redis-cli} { ...@@ -327,6 +327,8 @@ test {Migrate the last slot away from a node using redis-cli} {
} }
} }
foreach ip_or_localhost {127.0.0.1 localhost} {
# Test redis-cli --cluster create, add-node with cluster-port. # Test redis-cli --cluster create, add-node with cluster-port.
# Create five nodes, three with custom cluster_port and two with default values. # Create five nodes, three with custom cluster_port and two with default values.
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cluster-port [find_available_port $::baseport $::portcount]]] { start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cluster-port [find_available_port $::baseport $::portcount]]] {
...@@ -337,17 +339,12 @@ start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cl ...@@ -337,17 +339,12 @@ start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cl
# The first three are used to test --cluster create. # The first three are used to test --cluster create.
# The last two are used to test --cluster add-node # The last two are used to test --cluster add-node
set node1_rd [redis_client 0]
set node2_rd [redis_client -1]
set node3_rd [redis_client -2]
set node4_rd [redis_client -3]
set node5_rd [redis_client -4]
test {redis-cli --cluster create with cluster-port} { test "redis-cli -4 --cluster create using $ip_or_localhost with cluster-port" {
exec src/redis-cli --cluster-yes --cluster create \ exec src/redis-cli -4 --cluster-yes --cluster create \
127.0.0.1:[srv 0 port] \ $ip_or_localhost:[srv 0 port] \
127.0.0.1:[srv -1 port] \ $ip_or_localhost:[srv -1 port] \
127.0.0.1:[srv -2 port] $ip_or_localhost:[srv -2 port]
wait_for_condition 1000 50 { wait_for_condition 1000 50 {
[CI 0 cluster_state] eq {ok} && [CI 0 cluster_state] eq {ok} &&
...@@ -363,11 +360,11 @@ start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cl ...@@ -363,11 +360,11 @@ start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cl
assert_equal 3 [CI 2 cluster_known_nodes] assert_equal 3 [CI 2 cluster_known_nodes]
} }
test {redis-cli --cluster add-node with cluster-port} { test "redis-cli -4 --cluster add-node using $ip_or_localhost with cluster-port" {
# Adding node to the cluster (without cluster-port) # Adding node to the cluster (without cluster-port)
exec src/redis-cli --cluster-yes --cluster add-node \ exec src/redis-cli -4 --cluster-yes --cluster add-node \
127.0.0.1:[srv -3 port] \ $ip_or_localhost:[srv -3 port] \
127.0.0.1:[srv 0 port] $ip_or_localhost:[srv 0 port]
wait_for_cluster_size 4 wait_for_cluster_size 4
...@@ -381,9 +378,9 @@ start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cl ...@@ -381,9 +378,9 @@ start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cl
} }
# Adding node to the cluster (with cluster-port) # Adding node to the cluster (with cluster-port)
exec src/redis-cli --cluster-yes --cluster add-node \ exec src/redis-cli -4 --cluster-yes --cluster add-node \
127.0.0.1:[srv -4 port] \ $ip_or_localhost:[srv -4 port] \
127.0.0.1:[srv 0 port] $ip_or_localhost:[srv 0 port]
wait_for_cluster_size 5 wait_for_cluster_size 5
...@@ -411,6 +408,8 @@ start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cl ...@@ -411,6 +408,8 @@ start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cl
} }
} }
} ;# foreach ip_or_localhost
} ;# tags } ;# tags
set ::singledb $old_singledb set ::singledb $old_singledb
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment