]>
git.saurik.com Git - redis.git/blob - deps/hiredis/net.c
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>
52 /* Defined in hiredis.c */
53 void __redisSetError(redisContext
*c
, int type
, const char *str
);
55 static void __redisSetErrorFromErrno(redisContext
*c
, int type
, const char *prefix
) {
60 len
= snprintf(buf
,sizeof(buf
),"%s: ",prefix
);
61 strerror_r(errno
,buf
+len
,sizeof(buf
)-len
);
62 __redisSetError(c
,type
,buf
);
65 static int redisSetReuseAddr(redisContext
*c
, int fd
) {
67 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
68 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
75 static int redisCreateSocket(redisContext
*c
, int type
) {
77 if ((s
= socket(type
, SOCK_STREAM
, 0)) == -1) {
78 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
81 if (type
== AF_INET
) {
82 if (redisSetReuseAddr(c
,s
) == REDIS_ERR
) {
89 static int redisSetBlocking(redisContext
*c
, int fd
, int blocking
) {
92 /* Set the socket nonblocking.
93 * Note that fcntl(2) for F_GETFL and F_SETFL can't be
94 * interrupted by a signal. */
95 if ((flags
= fcntl(fd
, F_GETFL
)) == -1) {
96 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"fcntl(F_GETFL)");
102 flags
&= ~O_NONBLOCK
;
106 if (fcntl(fd
, F_SETFL
, flags
) == -1) {
107 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"fcntl(F_SETFL)");
114 static int redisSetTcpNoDelay(redisContext
*c
, int fd
) {
116 if (setsockopt(fd
, IPPROTO_TCP
, TCP_NODELAY
, &yes
, sizeof(yes
)) == -1) {
117 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"setsockopt(TCP_NODELAY)");
124 static int redisContextWaitReady(redisContext
*c
, int fd
, const struct timeval
*timeout
) {
126 struct timeval
*toptr
= NULL
;
129 /* Only use timeout when not NULL. */
130 if (timeout
!= NULL
) {
135 if (errno
== EINPROGRESS
) {
139 if (select(FD_SETSIZE
, NULL
, &wfd
, NULL
, toptr
) == -1) {
140 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"select(2)");
145 if (!FD_ISSET(fd
, &wfd
)) {
147 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
152 if (redisCheckSocketError(c
, fd
) != REDIS_OK
)
158 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
163 int redisCheckSocketError(redisContext
*c
, int fd
) {
165 socklen_t errlen
= sizeof(err
);
167 if (getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &err
, &errlen
) == -1) {
168 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"getsockopt(SO_ERROR)");
175 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,NULL
);
183 int redisContextSetTimeout(redisContext
*c
, struct timeval tv
) {
184 if (setsockopt(c
->fd
,SOL_SOCKET
,SO_RCVTIMEO
,&tv
,sizeof(tv
)) == -1) {
185 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"setsockopt(SO_RCVTIMEO)");
188 if (setsockopt(c
->fd
,SOL_SOCKET
,SO_SNDTIMEO
,&tv
,sizeof(tv
)) == -1) {
189 __redisSetErrorFromErrno(c
,REDIS_ERR_IO
,"setsockopt(SO_SNDTIMEO)");
195 int redisContextConnectTcp(redisContext
*c
, const char *addr
, int port
, struct timeval
*timeout
) {
197 char _port
[6]; /* strlen("65535"); */
198 struct addrinfo hints
, *servinfo
, *p
;
199 int blocking
= (c
->flags
& REDIS_BLOCK
);
201 snprintf(_port
, 6, "%d", port
);
202 memset(&hints
,0,sizeof(hints
));
203 hints
.ai_family
= AF_INET
;
204 hints
.ai_socktype
= SOCK_STREAM
;
206 if ((rv
= getaddrinfo(addr
,_port
,&hints
,&servinfo
)) != 0) {
207 __redisSetError(c
,REDIS_ERR_OTHER
,gai_strerror(rv
));
210 for (p
= servinfo
; p
!= NULL
; p
= p
->ai_next
) {
211 if ((s
= socket(p
->ai_family
,p
->ai_socktype
,p
->ai_protocol
)) == -1)
214 if (redisSetBlocking(c
,s
,0) != REDIS_OK
)
216 if (connect(s
,p
->ai_addr
,p
->ai_addrlen
) == -1) {
217 if (errno
== EHOSTUNREACH
) {
220 } else if (errno
== EINPROGRESS
&& !blocking
) {
223 if (redisContextWaitReady(c
,s
,timeout
) != REDIS_OK
)
227 if (blocking
&& redisSetBlocking(c
,s
,1) != REDIS_OK
)
229 if (redisSetTcpNoDelay(c
,s
) != REDIS_OK
)
233 c
->flags
|= REDIS_CONNECTED
;
239 snprintf(buf
,sizeof(buf
),"Can't create socket: %s",strerror(errno
));
240 __redisSetError(c
,REDIS_ERR_OTHER
,buf
);
247 freeaddrinfo(servinfo
);
248 return rv
; // Need to return REDIS_OK if alright
251 int redisContextConnectUnix(redisContext
*c
, const char *path
, struct timeval
*timeout
) {
253 int blocking
= (c
->flags
& REDIS_BLOCK
);
254 struct sockaddr_un sa
;
256 if ((s
= redisCreateSocket(c
,AF_LOCAL
)) < 0)
258 if (redisSetBlocking(c
,s
,0) != REDIS_OK
)
261 sa
.sun_family
= AF_LOCAL
;
262 strncpy(sa
.sun_path
,path
,sizeof(sa
.sun_path
)-1);
263 if (connect(s
, (struct sockaddr
*)&sa
, sizeof(sa
)) == -1) {
264 if (errno
== EINPROGRESS
&& !blocking
) {
267 if (redisContextWaitReady(c
,s
,timeout
) != REDIS_OK
)
272 /* Reset socket to be blocking after connect(2). */
273 if (blocking
&& redisSetBlocking(c
,s
,1) != REDIS_OK
)
277 c
->flags
|= REDIS_CONNECTED
;