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
This commit is contained in:
Matt Stancliff 2014-04-08 13:15:36 -04:00
parent 61eeedbe77
commit 37d25a392c
6 changed files with 56 additions and 2 deletions

View File

@ -165,6 +165,14 @@ redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
return ac; 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) { redisAsyncContext *redisAsyncConnectUnix(const char *path) {
redisContext *c; redisContext *c;
redisAsyncContext *ac; redisAsyncContext *ac;

View File

@ -102,6 +102,7 @@ typedef struct redisAsyncContext {
/* Functions that proxy to hiredis */ /* Functions that proxy to hiredis */
redisAsyncContext *redisAsyncConnect(const char *ip, int port); redisAsyncContext *redisAsyncConnect(const char *ip, int port);
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,char *source);
redisAsyncContext *redisAsyncConnectUnix(const char *path); redisAsyncContext *redisAsyncConnectUnix(const char *path);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);

View File

@ -1049,6 +1049,14 @@ redisContext *redisConnectNonBlock(const char *ip, int port) {
return c; 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 *redisConnectUnix(const char *path) {
redisContext *c; redisContext *c;

View File

@ -175,6 +175,7 @@ typedef struct redisContext {
redisContext *redisConnect(const char *ip, int port); redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectNonBlock(const char *ip, int port); redisContext *redisConnectNonBlock(const char *ip, int port);
redisContext *redisConnectBindNonBlock(const char *ip, int port, char *source);
redisContext *redisConnectUnix(const char *path); redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path); redisContext *redisConnectUnixNonBlock(const char *path);

38
net.c
View File

@ -250,10 +250,12 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
return REDIS_OK; 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; int s, rv;
char _port[6]; /* strlen("65535"); */ char _port[6]; /* strlen("65535"); */
struct addrinfo hints, *servinfo, *p; struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
int blocking = (c->flags & REDIS_BLOCK); int blocking = (c->flags & REDIS_BLOCK);
snprintf(_port, 6, "%d", port); snprintf(_port, 6, "%d", port);
@ -280,6 +282,28 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, const st
c->fd = s; c->fd = s;
if (redisSetBlocking(c,0) != REDIS_OK) if (redisSetBlocking(c,0) != REDIS_OK)
goto error; 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 (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
if (errno == EHOSTUNREACH) { if (errno == EHOSTUNREACH) {
redisContextCloseFd(c); redisContextCloseFd(c);
@ -314,6 +338,16 @@ end:
return rv; // Need to return REDIS_OK if alright 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 redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
int blocking = (c->flags & REDIS_BLOCK); int blocking = (c->flags & REDIS_BLOCK);
struct sockaddr_un sa; struct sockaddr_un sa;

2
net.h
View File

@ -42,6 +42,8 @@
int redisCheckSocketError(redisContext *c); int redisCheckSocketError(redisContext *c);
int redisContextSetTimeout(redisContext *c, const struct timeval tv); int redisContextSetTimeout(redisContext *c, const struct timeval tv);
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); 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 redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
int redisKeepAlive(redisContext *c, int interval); int redisKeepAlive(redisContext *c, int interval);