summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Stancliff <matt@genges.com>2014-04-08 13:15:36 -0400
committerMatt Stancliff <matt@genges.com>2014-04-08 19:37:54 -0400
commit37d25a392c9b9468e064a67c504939c9c4ea0031 (patch)
tree50905ce117ce77ebc8a127a7d43b979576f651da
parent61eeedbe77a7fef16e44b6fe215a689b7911044e (diff)
Add ability to bind source address on connect
Some environments require binding to specific source addresses instead of letting the system determine which IP a connection should originate from. Closes #233
-rw-r--r--async.c8
-rw-r--r--async.h1
-rw-r--r--hiredis.c8
-rw-r--r--hiredis.h1
-rw-r--r--net.c38
-rw-r--r--net.h2
6 files changed, 56 insertions, 2 deletions
diff --git a/async.c b/async.c
index f3b57e3..7fd17f2 100644
--- a/async.c
+++ b/async.c
@@ -165,6 +165,14 @@ redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
return ac;
}
+redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
+ char *source_addr) {
+ redisContext *c = redisConnectBindNonBlock(ip,port,source_addr);
+ redisAsyncContext *ac = redisAsyncInitialize(c);
+ __redisAsyncCopyError(ac);
+ return ac;
+}
+
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
redisContext *c;
redisAsyncContext *ac;
diff --git a/async.h b/async.h
index 268274e..1df2ff0 100644
--- a/async.h
+++ b/async.h
@@ -102,6 +102,7 @@ typedef struct redisAsyncContext {
/* Functions that proxy to hiredis */
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
+redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,char *source);
redisAsyncContext *redisAsyncConnectUnix(const char *path);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
diff --git a/hiredis.c b/hiredis.c
index 9b74b5b..beed8e9 100644
--- a/hiredis.c
+++ b/hiredis.c
@@ -1049,6 +1049,14 @@ redisContext *redisConnectNonBlock(const char *ip, int port) {
return c;
}
+redisContext *redisConnectBindNonBlock(const char *ip, int port,
+ char *source_addr) {
+ redisContext *c = redisContextInit();
+ c->flags &= ~REDIS_BLOCK;
+ redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
+ return c;
+}
+
redisContext *redisConnectUnix(const char *path) {
redisContext *c;
diff --git a/hiredis.h b/hiredis.h
index c65098b..6d24213 100644
--- a/hiredis.h
+++ b/hiredis.h
@@ -175,6 +175,7 @@ typedef struct redisContext {
redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectNonBlock(const char *ip, int port);
+redisContext *redisConnectBindNonBlock(const char *ip, int port, char *source);
redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path);
diff --git a/net.c b/net.c
index c26f4cc..6cbae48 100644
--- a/net.c
+++ b/net.c
@@ -250,10 +250,12 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
return REDIS_OK;
}
-int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout) {
+static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
+ const struct timeval *timeout,
+ char *source_addr) {
int s, rv;
char _port[6]; /* strlen("65535"); */
- struct addrinfo hints, *servinfo, *p;
+ struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
int blocking = (c->flags & REDIS_BLOCK);
snprintf(_port, 6, "%d", port);
@@ -280,6 +282,28 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, const st
c->fd = s;
if (redisSetBlocking(c,0) != REDIS_OK)
goto error;
+ if (source_addr) {
+ int bound = 0;
+ /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
+ if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0) {
+ char buf[128];
+ snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
+ goto error;
+ }
+ for (b = bservinfo; b != NULL; b = b->ai_next) {
+ if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
+ bound = 1;
+ break;
+ }
+ }
+ if (!bound) {
+ char buf[128];
+ snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
+ goto error;
+ }
+ }
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
if (errno == EHOSTUNREACH) {
redisContextCloseFd(c);
@@ -314,6 +338,16 @@ end:
return rv; // Need to return REDIS_OK if alright
}
+int redisContextConnectTcp(redisContext *c, const char *addr, int port,
+ const struct timeval *timeout) {
+ return _redisContextConnectTcp(c, addr, port, timeout, NULL);
+}
+
+int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
+ const struct timeval *timeout, char *source_addr) {
+ return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
+}
+
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
int blocking = (c->flags & REDIS_BLOCK);
struct sockaddr_un sa;
diff --git a/net.h b/net.h
index af7da11..370a1ff 100644
--- a/net.h
+++ b/net.h
@@ -42,6 +42,8 @@
int redisCheckSocketError(redisContext *c);
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
+int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
+ const struct timeval *timeout, char *source_addr);
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
int redisKeepAlive(redisContext *c, int interval);