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: Viktor Söderqvist <viktor.soderqvist@est.tech>
This commit is contained in:
Binbin 2023-12-24 16:40:34 +08:00 committed by GitHub
parent 23e980e77a
commit 09e0d338f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 21 deletions

View File

@ -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
* 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 flags)
{
@ -249,9 +253,20 @@ int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
memset(&hints,0,sizeof(hints));
if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST;
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 */
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));
return ANET_ERR;
}

View File

@ -40,6 +40,8 @@
/* Flags used with certain functions. */
#define ANET_NONE 0
#define ANET_IP_ONLY (1<<0)
#define ANET_PREFER_IPV4 (1<<1)
#define ANET_PREFER_IPV6 (1<<2)
#if defined(__sun) || defined(_AIX)
#define AF_LOCAL AF_UNIX

View File

@ -275,6 +275,8 @@ static struct config {
char *server_version;
char *test_hint;
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;
/* User preferences. */
@ -2768,6 +2770,10 @@ static int parseOptions(int argc, char **argv) {
config.set_errcode = 1;
} else if (!strcmp(argv[i],"--verbose")) {
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) {
if (CLUSTER_MANAGER_MODE()) usage(1);
char *cmd = argv[++i];
@ -2952,6 +2958,11 @@ static int parseOptions(int argc, char **argv) {
exit(1);
}
if (config.prefer_ipv4 && config.prefer_ipv6) {
fprintf(stderr, "Options -4 and -6 are mutually exclusive.\n");
exit(1);
}
return i;
}
@ -3028,6 +3039,8 @@ static void usage(int err) {
" -D <delimiter> Delimiter between responses for raw formatting (default: \\n).\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\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"
" --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty).\n"
@ -7071,7 +7084,10 @@ assign_replicas:
first = node;
/* Although hiredis supports connecting to a hostname, CLUSTER
* 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)
{
fprintf(stderr, "Invalid IP address or hostname specified: %s\n", first->ip);
@ -7266,7 +7282,10 @@ static int clusterManagerCommandAddNode(int argc, char **argv) {
"join the cluster.\n", ip, port);
/* CLUSTER MEET requires an IP address, so we do a DNS lookup here. */
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);
success = 0;
goto cleanup;
@ -9862,6 +9881,8 @@ int main(int argc, char **argv) {
config.no_auth_warning = 0;
config.in_multi = 0;
config.server_version = NULL;
config.prefer_ipv4 = 0;
config.prefer_ipv6 = 0;
config.cluster_manager_command.name = NULL;
config.cluster_manager_command.argc = 0;
config.cluster_manager_command.argv = NULL;

View File

@ -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.
# 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]]] {
@ -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 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} {
exec src/redis-cli --cluster-yes --cluster create \
127.0.0.1:[srv 0 port] \
127.0.0.1:[srv -1 port] \
127.0.0.1:[srv -2 port]
test "redis-cli -4 --cluster create using $ip_or_localhost with cluster-port" {
exec src/redis-cli -4 --cluster-yes --cluster create \
$ip_or_localhost:[srv 0 port] \
$ip_or_localhost:[srv -1 port] \
$ip_or_localhost:[srv -2 port]
wait_for_condition 1000 50 {
[CI 0 cluster_state] eq {ok} &&
@ -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]
}
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)
exec src/redis-cli --cluster-yes --cluster add-node \
127.0.0.1:[srv -3 port] \
127.0.0.1:[srv 0 port]
exec src/redis-cli -4 --cluster-yes --cluster add-node \
$ip_or_localhost:[srv -3 port] \
$ip_or_localhost:[srv 0 port]
wait_for_cluster_size 4
@ -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)
exec src/redis-cli --cluster-yes --cluster add-node \
127.0.0.1:[srv -4 port] \
127.0.0.1:[srv 0 port]
exec src/redis-cli -4 --cluster-yes --cluster add-node \
$ip_or_localhost:[srv -4 port] \
$ip_or_localhost:[srv 0 port]
wait_for_cluster_size 5
@ -411,6 +408,8 @@ start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cl
}
}
} ;# foreach ip_or_localhost
} ;# tags
set ::singledb $old_singledb