2010-11-03 16:03:04 +01:00
/* Extracted from anet.c to work properly with Hiredis error reporting.
*
2012-02-19 20:26:36 +01:00
* Copyright ( c ) 2006 - 2011 , Salvatore Sanfilippo < antirez at gmail dot com >
* Copyright ( c ) 2010 - 2011 , Pieter Noordhuis < pcnoordhuis at gmail dot com >
2010-11-03 16:03:04 +01:00
*
2010-12-16 23:32:02 +01:00
* All rights reserved .
2011-04-19 23:07:36 +02:00
*
2010-11-03 16:03:04 +01:00
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* * Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
# include "fmacros.h"
# include <sys/types.h>
# include <sys/socket.h>
2011-04-19 23:07:36 +02:00
# include <sys/select.h>
2010-11-03 16:03:04 +01:00
# include <sys/un.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
# include <arpa/inet.h>
# include <unistd.h>
# include <fcntl.h>
# include <string.h>
# include <netdb.h>
# include <errno.h>
# include <stdarg.h>
# include <stdio.h>
2010-12-16 23:32:02 +01:00
# include "net.h"
2010-11-03 16:03:04 +01:00
# include "sds.h"
2012-02-19 20:26:36 +01:00
/* Defined in hiredis.c */
void __redisSetError ( redisContext * c , int type , const char * str ) ;
static void __redisSetErrorFromErrno ( redisContext * c , int type , const char * prefix ) {
char buf [ 128 ] ;
size_t len = 0 ;
if ( prefix ! = NULL )
len = snprintf ( buf , sizeof ( buf ) , " %s: " , prefix ) ;
strerror_r ( errno , buf + len , sizeof ( buf ) - len ) ;
__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 ;
}
2010-11-03 16:03:04 +01:00
static int redisCreateSocket ( redisContext * c , int type ) {
2012-02-19 20:26:36 +01:00
int s ;
2010-11-03 16:03:04 +01:00
if ( ( s = socket ( type , SOCK_STREAM , 0 ) ) = = - 1 ) {
2012-02-19 20:26:36 +01:00
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , NULL ) ;
2010-11-03 16:03:04 +01:00
return REDIS_ERR ;
}
if ( type = = AF_INET ) {
2012-02-19 20:26:36 +01:00
if ( redisSetReuseAddr ( c , s ) = = REDIS_ERR ) {
2010-11-03 16:03:04 +01:00
return REDIS_ERR ;
}
}
return s ;
}
2011-04-19 23:07:36 +02:00
static int redisSetBlocking ( redisContext * c , int fd , int blocking ) {
2010-11-03 16:03:04 +01:00
int flags ;
/* Set the socket nonblocking.
* Note that fcntl ( 2 ) for F_GETFL and F_SETFL can ' t be
* interrupted by a signal . */
if ( ( flags = fcntl ( fd , F_GETFL ) ) = = - 1 ) {
2012-02-19 20:26:36 +01:00
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , " fcntl(F_GETFL) " ) ;
2010-11-03 16:03:04 +01:00
close ( fd ) ;
return REDIS_ERR ;
}
2011-04-19 23:07:36 +02:00
if ( blocking )
flags & = ~ O_NONBLOCK ;
else
flags | = O_NONBLOCK ;
if ( fcntl ( fd , F_SETFL , flags ) = = - 1 ) {
2012-02-19 20:26:36 +01:00
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , " fcntl(F_SETFL) " ) ;
2010-11-03 16:03:04 +01:00
close ( fd ) ;
return REDIS_ERR ;
}
return REDIS_OK ;
}
static int redisSetTcpNoDelay ( redisContext * c , int fd ) {
int yes = 1 ;
if ( setsockopt ( fd , IPPROTO_TCP , TCP_NODELAY , & yes , sizeof ( yes ) ) = = - 1 ) {
2012-02-19 20:26:36 +01:00
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , " setsockopt(TCP_NODELAY) " ) ;
2011-04-19 23:07:36 +02:00
close ( fd ) ;
2010-11-03 16:03:04 +01:00
return REDIS_ERR ;
}
return REDIS_OK ;
}
2011-04-19 23:07:36 +02:00
static int redisContextWaitReady ( redisContext * c , int fd , const struct timeval * timeout ) {
struct timeval to ;
struct timeval * toptr = NULL ;
fd_set wfd ;
/* Only use timeout when not NULL. */
if ( timeout ! = NULL ) {
to = * timeout ;
toptr = & to ;
}
if ( errno = = EINPROGRESS ) {
FD_ZERO ( & wfd ) ;
FD_SET ( fd , & wfd ) ;
if ( select ( FD_SETSIZE , NULL , & wfd , NULL , toptr ) = = - 1 ) {
2012-02-19 20:26:36 +01:00
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , " select(2) " ) ;
2011-04-19 23:07:36 +02:00
close ( fd ) ;
return REDIS_ERR ;
}
if ( ! FD_ISSET ( fd , & wfd ) ) {
errno = ETIMEDOUT ;
2012-02-19 20:26:36 +01:00
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , NULL ) ;
2011-04-19 23:07:36 +02:00
close ( fd ) ;
return REDIS_ERR ;
}
2012-02-19 20:26:36 +01:00
if ( redisCheckSocketError ( c , fd ) ! = REDIS_OK )
2011-04-19 23:07:36 +02:00
return REDIS_ERR ;
return REDIS_OK ;
}
2012-02-19 20:26:36 +01:00
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , NULL ) ;
2011-04-19 23:07:36 +02:00
close ( fd ) ;
return REDIS_ERR ;
}
2012-02-19 20:26:36 +01:00
int redisCheckSocketError ( redisContext * c , int fd ) {
int err = 0 ;
socklen_t errlen = sizeof ( err ) ;
if ( getsockopt ( fd , SOL_SOCKET , SO_ERROR , & err , & errlen ) = = - 1 ) {
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , " getsockopt(SO_ERROR) " ) ;
close ( fd ) ;
return REDIS_ERR ;
}
if ( err ) {
errno = err ;
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , NULL ) ;
close ( fd ) ;
return REDIS_ERR ;
}
return REDIS_OK ;
}
2011-04-19 23:07:36 +02:00
int redisContextSetTimeout ( redisContext * c , struct timeval tv ) {
if ( setsockopt ( c - > fd , SOL_SOCKET , SO_RCVTIMEO , & tv , sizeof ( tv ) ) = = - 1 ) {
2012-02-19 20:26:36 +01:00
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , " setsockopt(SO_RCVTIMEO) " ) ;
2011-04-19 23:07:36 +02:00
return REDIS_ERR ;
}
if ( setsockopt ( c - > fd , SOL_SOCKET , SO_SNDTIMEO , & tv , sizeof ( tv ) ) = = - 1 ) {
2012-02-19 20:26:36 +01:00
__redisSetErrorFromErrno ( c , REDIS_ERR_IO , " setsockopt(SO_SNDTIMEO) " ) ;
2011-04-19 23:07:36 +02:00
return REDIS_ERR ;
}
return REDIS_OK ;
}
int redisContextConnectTcp ( redisContext * c , const char * addr , int port , struct timeval * timeout ) {
2012-02-19 20:26:36 +01:00
int s , rv ;
char _port [ 6 ] ; /* strlen("65535"); */
struct addrinfo hints , * servinfo , * p ;
2010-11-03 16:03:04 +01:00
int blocking = ( c - > flags & REDIS_BLOCK ) ;
2012-02-19 20:26:36 +01:00
snprintf ( _port , 6 , " %d " , port ) ;
memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_family = AF_INET ;
hints . ai_socktype = SOCK_STREAM ;
2010-11-03 16:03:04 +01:00
2012-02-19 20:26:36 +01:00
if ( ( rv = getaddrinfo ( addr , _port , & hints , & servinfo ) ) ! = 0 ) {
__redisSetError ( c , REDIS_ERR_OTHER , gai_strerror ( rv ) ) ;
return REDIS_ERR ;
2010-11-03 16:03:04 +01:00
}
2012-02-19 20:26:36 +01:00
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 = = EHOSTUNREACH ) {
close ( s ) ;
continue ;
} else if ( errno = = EINPROGRESS & & ! blocking ) {
/* This is ok. */
} else {
if ( redisContextWaitReady ( c , s , timeout ) ! = REDIS_OK )
goto error ;
}
2010-11-03 16:03:04 +01:00
}
2012-02-19 20:26:36 +01:00
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 ;
2010-11-03 16:03:04 +01:00
}
2012-02-19 20:26:36 +01:00
error :
rv = REDIS_ERR ;
end :
freeaddrinfo ( servinfo ) ;
return rv ; // Need to return REDIS_OK if alright
2010-11-03 16:03:04 +01:00
}
2011-04-19 23:07:36 +02:00
int redisContextConnectUnix ( redisContext * c , const char * path , struct timeval * timeout ) {
2010-11-03 16:03:04 +01:00
int s ;
int blocking = ( c - > flags & REDIS_BLOCK ) ;
struct sockaddr_un sa ;
2011-04-19 23:07:36 +02:00
if ( ( s = redisCreateSocket ( c , AF_LOCAL ) ) < 0 )
2010-11-03 16:03:04 +01:00
return REDIS_ERR ;
2011-04-19 23:07:36 +02:00
if ( redisSetBlocking ( c , s , 0 ) ! = REDIS_OK )
2010-11-03 16:03:04 +01:00
return REDIS_ERR ;
sa . sun_family = AF_LOCAL ;
strncpy ( sa . sun_path , path , sizeof ( sa . sun_path ) - 1 ) ;
if ( connect ( s , ( struct sockaddr * ) & sa , sizeof ( sa ) ) = = - 1 ) {
if ( errno = = EINPROGRESS & & ! blocking ) {
/* This is ok. */
} else {
2011-04-19 23:07:36 +02:00
if ( redisContextWaitReady ( c , s , timeout ) ! = REDIS_OK )
return REDIS_ERR ;
2010-11-03 16:03:04 +01:00
}
}
2011-04-19 23:07:36 +02:00
/* Reset socket to be blocking after connect(2). */
if ( blocking & & redisSetBlocking ( c , s , 1 ) ! = REDIS_OK )
return REDIS_ERR ;
2010-11-03 16:03:04 +01:00
c - > fd = s ;
2010-12-16 23:32:02 +01:00
c - > flags | = REDIS_CONNECTED ;
2010-11-03 16:03:04 +01:00
return REDIS_OK ;
}