summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hiredis.c6
-rw-r--r--hiredis.h7
-rw-r--r--net.c30
3 files changed, 32 insertions, 11 deletions
diff --git a/hiredis.c b/hiredis.c
index 6c48d66..c0968ed 100644
--- a/hiredis.c
+++ b/hiredis.c
@@ -814,6 +814,12 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
if (options->options & REDIS_OPT_NOAUTOFREEREPLIES) {
c->flags |= REDIS_NO_AUTO_FREE_REPLIES;
}
+ if (options->options & REDIS_OPT_PREFER_IPV4) {
+ c->flags |= REDIS_PREFER_IPV4;
+ }
+ if (options->options & REDIS_OPT_PREFER_IPV6) {
+ c->flags |= REDIS_PREFER_IPV6;
+ }
/* Set any user supplied RESP3 PUSH handler or use freeReplyObject
* as a default unless specifically flagged that we don't want one. */
diff --git a/hiredis.h b/hiredis.h
index 7e2a84d..1cbdde4 100644
--- a/hiredis.h
+++ b/hiredis.h
@@ -92,6 +92,11 @@ typedef long long ssize_t;
/* Flag that indicates the user does not want replies to be automatically freed */
#define REDIS_NO_AUTO_FREE_REPLIES 0x400
+/* Flags to prefer IPv6 or IPv4 when doing DNS lookup. (If both are set,
+ * AF_UNSPEC is used.) */
+#define REDIS_PREFER_IPV4 0x800
+#define REDIS_PREFER_IPV6 0x1000
+
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
/* number of times we retry to connect in the case of EADDRNOTAVAIL and
@@ -149,6 +154,8 @@ struct redisSsl;
#define REDIS_OPT_NONBLOCK 0x01
#define REDIS_OPT_REUSEADDR 0x02
+#define REDIS_OPT_PREFER_IPV4 0x04
+#define REDIS_OPT_PREFER_IPV6 0x08
/**
* Don't automatically free the async object on a connection failure,
diff --git a/net.c b/net.c
index 8378c28..8cd3d71 100644
--- a/net.c
+++ b/net.c
@@ -439,17 +439,25 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
- /* Try with IPv6 if no IPv4 address was found. We do it in this order since
- * in a Redis client you can't afford to test if you have IPv6 connectivity
- * as this would add latency to every connect. Otherwise a more sensible
- * route could be: Use IPv6 if both addresses are available and there is IPv6
- * connectivity. */
- if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
- hints.ai_family = AF_INET6;
- if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
- __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
- return REDIS_ERR;
- }
+ /* DNS lookup. To use dual stack, set both flags to prefer both IPv4 and
+ * IPv6. By default, for historical reasons, we try IPv4 first and then we
+ * try IPv6 only if no IPv4 address was found. */
+ if (c->flags & REDIS_PREFER_IPV6 && c->flags & REDIS_PREFER_IPV4)
+ hints.ai_family = AF_UNSPEC;
+ else if (c->flags & REDIS_PREFER_IPV6)
+ hints.ai_family = AF_INET6;
+ else
+ hints.ai_family = AF_INET;
+
+ rv = getaddrinfo(c->tcp.host, _port, &hints, &servinfo);
+ if (rv != 0 && hints.ai_family != AF_UNSPEC) {
+ /* Try again with the other IP version. */
+ hints.ai_family = (hints.ai_family == AF_INET) ? AF_INET6 : AF_INET;
+ rv = getaddrinfo(c->tcp.host, _port, &hints, &servinfo);
+ }
+ if (rv != 0) {
+ __redisSetError(c, REDIS_ERR_OTHER, gai_strerror(rv));
+ return REDIS_ERR;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
addrretry: