1 /* Extracted from anet.c to work properly with Hiredis error reporting.
3 * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4 * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * * Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of Redis nor the names of its contributors may be used
17 * to endorse or promote products derived from this software without
18 * specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/select.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
40 #include <arpa/inet.h>
54 /* Defined in hiredis.c */
55 void __redisSetError(redisContext
*c
, int type
, const char *str
);
57 static void __redisSetErrorFromErrno(redisContext
*c
, int type
, const char *prefix
) {
62 len
= snprintf(buf
,sizeof(buf
),"%s: ",prefix
);
63 strerror_r(errno
,buf
+len
,sizeof(buf
)-len
);
64 __redisSetError(c
,type
,buf
);
67 static int redisSetReuseAddr(redisContext
*c
, int fd
) {
69 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
70 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
77 static int redisCreateSocket(redisContext
*c
, int type
) {
79 if ((s
= socket(type
, SOCK_STREAM
, 0)) == -1) {
80 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
83 if (type
== AF_INET
) {
84 if (redisSetReuseAddr(c
,s
) == REDIS_ERR
) {
91 static int redisSetBlocking(redisContext
*c
, int fd
, int blocking
) {
94 /* Set the socket nonblocking.
95 * Note that fcntl(2) for F_GETFL and F_SETFL can't be
96 * interrupted by a signal. */
97 if ((flags
= fcntl(fd
, F_GETFL
)) == -1) {
98 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"fcntl(F_GETFL)");
104 flags
&= ~O_NONBLOCK
;
108 if (fcntl(fd
, F_SETFL
, flags
) == -1) {
109 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"fcntl(F_SETFL)");
116 static int redisSetTcpNoDelay(redisContext
*c
, int fd
) {
118 if (setsockopt(fd
, IPPROTO_TCP
, TCP_NODELAY
, &yes
, sizeof(yes
)) == -1) {
119 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"setsockopt(TCP_NODELAY)");
126 #define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
128 static int redisContextWaitReady(redisContext
*c
, int fd
, const struct timeval
*timeout
) {
129 struct pollfd wfd
[1];
134 wfd
[0].events
= POLLOUT
;
136 /* Only use timeout when not NULL. */
137 if (timeout
!= NULL
) {
138 if (timeout
->tv_usec
> 1000000 || timeout
->tv_sec
> __MAX_MSEC
) {
143 msec
= (timeout
->tv_sec
* 1000) + ((timeout
->tv_usec
+ 999) / 1000);
145 if (msec
< 0 || msec
> INT_MAX
) {
150 if (errno
== EINPROGRESS
) {
153 if ((res
= poll(wfd
, 1, msec
)) == -1) {
154 __redisSetErrorFromErrno(c
, REDIS_ERR_IO
, "poll(2)");
157 } else if (res
== 0) {
159 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
164 if (redisCheckSocketError(c
, fd
) != REDIS_OK
)
170 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
175 int redisCheckSocketError(redisContext
*c
, int fd
) {
177 socklen_t errlen
= sizeof(err
);
179 if (getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &err
, &errlen
) == -1) {
180 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"getsockopt(SO_ERROR)");
187 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
195 int redisContextSetTimeout(redisContext
*c
, struct timeval tv
) {
196 if (setsockopt(c
->fd
,SOL_SOCKET
,SO_RCVTIMEO
,&tv
,sizeof(tv
)) == -1) {
197 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"setsockopt(SO_RCVTIMEO)");
200 if (setsockopt(c
->fd
,SOL_SOCKET
,SO_SNDTIMEO
,&tv
,sizeof(tv
)) == -1) {
201 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"setsockopt(SO_SNDTIMEO)");
207 int redisContextConnectTcp(redisContext
*c
, const char *addr
, int port
, struct timeval
*timeout
) {
209 char _port
[6]; /* strlen("65535"); */
210 struct addrinfo hints
, *servinfo
, *p
;
211 int blocking
= (c
->flags
& REDIS_BLOCK
);
213 snprintf(_port
, 6, "%d", port
);
214 memset(&hints
,0,sizeof(hints
));
215 hints
.ai_family
= AF_INET
;
216 hints
.ai_socktype
= SOCK_STREAM
;
218 if ((rv
= getaddrinfo(addr
,_port
,&hints
,&servinfo
)) != 0) {
219 __redisSetError(c
,REDIS_ERR_OTHER
,gai_strerror(rv
));
222 for (p
= servinfo
; p
!= NULL
; p
= p
->ai_next
) {
223 if ((s
= socket(p
->ai_family
,p
->ai_socktype
,p
->ai_protocol
)) == -1)
226 if (redisSetBlocking(c
,s
,0) != REDIS_OK
)
228 if (connect(s
,p
->ai_addr
,p
->ai_addrlen
) == -1) {
229 if (errno
== EHOSTUNREACH
) {
232 } else if (errno
== EINPROGRESS
&& !blocking
) {
235 if (redisContextWaitReady(c
,s
,timeout
) != REDIS_OK
)
239 if (blocking
&& redisSetBlocking(c
,s
,1) != REDIS_OK
)
241 if (redisSetTcpNoDelay(c
,s
) != REDIS_OK
)
245 c
->flags
|= REDIS_CONNECTED
;
251 snprintf(buf
,sizeof(buf
),"Can't create socket: %s",strerror(errno
));
252 __redisSetError(c
,REDIS_ERR_OTHER
,buf
);
259 freeaddrinfo(servinfo
);
260 return rv
; // Need to return REDIS_OK if alright
263 int redisContextConnectUnix(redisContext
*c
, const char *path
, struct timeval
*timeout
) {
265 int blocking
= (c
->flags
& REDIS_BLOCK
);
266 struct sockaddr_un sa
;
268 if ((s
= redisCreateSocket(c
,AF_LOCAL
)) < 0)
270 if (redisSetBlocking(c
,s
,0) != REDIS_OK
)
273 sa
.sun_family
= AF_LOCAL
;
274 strncpy(sa
.sun_path
,path
,sizeof(sa
.sun_path
)-1);
275 if (connect(s
, (struct sockaddr
*)&sa
, sizeof(sa
)) == -1) {
276 if (errno
== EINPROGRESS
&& !blocking
) {
279 if (redisContextWaitReady(c
,s
,timeout
) != REDIS_OK
)
284 /* Reset socket to be blocking after connect(2). */
285 if (blocking
&& redisSetBlocking(c
,s
,1) != REDIS_OK
)
289 c
->flags
|= REDIS_CONNECTED
;