diff options
author | Matt Stancliff <matt@genges.com> | 2014-04-08 13:15:36 -0400 |
---|---|---|
committer | Matt Stancliff <matt@genges.com> | 2014-04-08 19:37:54 -0400 |
commit | 37d25a392c9b9468e064a67c504939c9c4ea0031 (patch) | |
tree | 50905ce117ce77ebc8a127a7d43b979576f651da | |
parent | 61eeedbe77a7fef16e44b6fe215a689b7911044e (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.c | 8 | ||||
-rw-r--r-- | async.h | 1 | ||||
-rw-r--r-- | hiredis.c | 8 | ||||
-rw-r--r-- | hiredis.h | 1 | ||||
-rw-r--r-- | net.c | 38 | ||||
-rw-r--r-- | net.h | 2 |
6 files changed, 56 insertions, 2 deletions
@@ -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; @@ -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); @@ -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; @@ -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); @@ -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; @@ -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); |