From b4664b41c71fbcd7cda8644fd5e26ebf479d5df0 Mon Sep 17 00:00:00 2001 From: Geoff Garside Date: Fri, 17 Jun 2011 17:41:28 +0100 Subject: Add redisSetReuseAddr(c, fd) static function. Extract setting SO_REUSEADDR socket option into separate function so the same code can be more easily used by redisCreateSocket and other functions. --- net.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net.c b/net.c index 98eee5d..9364851 100644 --- a/net.c +++ b/net.c @@ -62,16 +62,24 @@ static void __redisSetErrorFromErrno(redisContext *c, int type, const char *pref __redisSetError(c,type,buf); } +static int redisSetReuseAddr(redisContext *c, int fd) { + int on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + close(fd); + return REDIS_ERR; + } + return REDIS_OK; +} + static int redisCreateSocket(redisContext *c, int type) { - int s, on = 1; + int s; if ((s = socket(type, SOCK_STREAM, 0)) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); return REDIS_ERR; } if (type == AF_INET) { - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - close(s); + if (redisSetReuseAddr(c,s) == REDIS_ERR) { return REDIS_ERR; } } -- cgit v1.2.3 From 3afe2585de94e82fd83c2b6a5534cf1e008dd083 Mon Sep 17 00:00:00 2001 From: Geoff Garside Date: Fri, 17 Jun 2011 19:26:46 +0100 Subject: Use getaddrinfo(3) in redisContextConnectTcp. Change redisContextConnectTcp() function to use getaddrinfo(3) to perform address resolution, socket creation and connection. Resolved addresses are limited to those reachable by the AF_INET family. --- net.c | 80 ++++++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/net.c b/net.c index 9364851..9298d2d 100644 --- a/net.c +++ b/net.c @@ -187,50 +187,56 @@ int redisContextSetTimeout(redisContext *c, struct timeval tv) { } int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) { - int s; + int s, rv; + char _port[6]; /* strlen("65535"); */ + struct addrinfo hints, *servinfo, *p; int blocking = (c->flags & REDIS_BLOCK); - struct sockaddr_in sa; - if ((s = redisCreateSocket(c,AF_INET)) < 0) - return REDIS_ERR; - if (redisSetBlocking(c,s,0) != REDIS_OK) - return REDIS_ERR; + snprintf(_port, 6, "%d", port); + memset(&hints,0,sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - if (inet_aton(addr, &sa.sin_addr) == 0) { - struct hostent *he; - - he = gethostbyname(addr); - if (he == NULL) { - char buf[128]; - snprintf(buf,sizeof(buf),"Can't resolve: %s", addr); - __redisSetError(c,REDIS_ERR_OTHER,buf); - close(s); - return REDIS_ERR; - } - memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); + if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) { + __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv)); + return REDIS_ERR; } - - if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { - if (errno == EINPROGRESS && !blocking) { - /* This is ok. */ - } else { - if (redisContextWaitReady(c,s,timeout) != REDIS_OK) - return REDIS_ERR; + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) + continue; + + if (redisSetBlocking(c,s,0) != REDIS_OK) + goto error; + if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { + if (errno == EINPROGRESS && !blocking) { + /* This is ok. */ + } else { + if (redisContextWaitReady(c,s,timeout) != REDIS_OK) + goto error; + } } + if (blocking && redisSetBlocking(c,s,1) != REDIS_OK) + goto error; + if (redisSetTcpNoDelay(c,s) != REDIS_OK) + goto error; + + c->fd = s; + c->flags |= REDIS_CONNECTED; + rv = REDIS_OK; + goto end; + } + if (p == NULL) { + char buf[128]; + snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno)); + __redisSetError(c,REDIS_ERR_OTHER,buf); + goto error; } - /* Reset socket to be blocking after connect(2). */ - if (blocking && redisSetBlocking(c,s,1) != REDIS_OK) - return REDIS_ERR; - - if (redisSetTcpNoDelay(c,s) != REDIS_OK) - return REDIS_ERR; - - c->fd = s; - c->flags |= REDIS_CONNECTED; - return REDIS_OK; +error: + rv = REDIS_ERR; +end: + freeaddrinfo(servinfo); + return rv; // Need to return REDIS_OK if alright } int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) { -- cgit v1.2.3 From c4ed06d90c2d25ccbb709b3d7c9c848aa732de66 Mon Sep 17 00:00:00 2001 From: Geoff Garside Date: Sat, 18 Jun 2011 14:08:25 +0100 Subject: Fix incorrect "no route to host" errors. If getaddrinfo(3) includes an AF_INET6 address before an AF_INET address on a host with only IPv4 network connectivity then the redisContextConnectTcp call would fail with "no route to host". This commit fixes this issue by specifically handling the errno EHOSTUNREACH error and entering another iteration of the addrinfo loop. This will allow following AF_INET addresses to be attempted. --- net.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net.c b/net.c index 9298d2d..ef3879c 100644 --- a/net.c +++ b/net.c @@ -208,7 +208,10 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct t if (redisSetBlocking(c,s,0) != REDIS_OK) goto error; if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { - if (errno == EINPROGRESS && !blocking) { + if (errno == EHOSTUNREACH) { + close(s); + continue; + } else if (errno == EINPROGRESS && !blocking) { /* This is ok. */ } else { if (redisContextWaitReady(c,s,timeout) != REDIS_OK) -- cgit v1.2.3