xref: /redis-3.2.3/deps/hiredis/net.c (revision 56c1f880)
1 /* Extracted from anet.c to work properly with Hiredis error reporting.
2  *
3  * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4  * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
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.
19  *
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.
31  */
32 
33 #include "fmacros.h"
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/select.h>
37 #include <sys/un.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
40 #include <arpa/inet.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <string.h>
44 #include <netdb.h>
45 #include <errno.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <poll.h>
49 #include <limits.h>
50 
51 #include "net.h"
52 #include "sds.h"
53 
54 /* Defined in hiredis.c */
55 void __redisSetError(redisContext *c, int type, const char *str);
56 
redisContextCloseFd(redisContext * c)57 static void redisContextCloseFd(redisContext *c) {
58     if (c && c->fd >= 0) {
59         close(c->fd);
60         c->fd = -1;
61     }
62 }
63 
__redisSetErrorFromErrno(redisContext * c,int type,const char * prefix)64 static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
65     char buf[128] = { 0 };
66     size_t len = 0;
67 
68     if (prefix != NULL)
69         len = snprintf(buf,sizeof(buf),"%s: ",prefix);
70     strerror_r(errno,buf+len,sizeof(buf)-len);
71     __redisSetError(c,type,buf);
72 }
73 
redisSetReuseAddr(redisContext * c)74 static int redisSetReuseAddr(redisContext *c) {
75     int on = 1;
76     if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
77         __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
78         redisContextCloseFd(c);
79         return REDIS_ERR;
80     }
81     return REDIS_OK;
82 }
83 
redisCreateSocket(redisContext * c,int type)84 static int redisCreateSocket(redisContext *c, int type) {
85     int s;
86     if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
87         __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
88         return REDIS_ERR;
89     }
90     c->fd = s;
91     if (type == AF_INET) {
92         if (redisSetReuseAddr(c) == REDIS_ERR) {
93             return REDIS_ERR;
94         }
95     }
96     return REDIS_OK;
97 }
98 
redisSetBlocking(redisContext * c,int blocking)99 static int redisSetBlocking(redisContext *c, int blocking) {
100     int flags;
101 
102     /* Set the socket nonblocking.
103      * Note that fcntl(2) for F_GETFL and F_SETFL can't be
104      * interrupted by a signal. */
105     if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
106         __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
107         redisContextCloseFd(c);
108         return REDIS_ERR;
109     }
110 
111     if (blocking)
112         flags &= ~O_NONBLOCK;
113     else
114         flags |= O_NONBLOCK;
115 
116     if (fcntl(c->fd, F_SETFL, flags) == -1) {
117         __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
118         redisContextCloseFd(c);
119         return REDIS_ERR;
120     }
121     return REDIS_OK;
122 }
123 
redisKeepAlive(redisContext * c,int interval)124 int redisKeepAlive(redisContext *c, int interval) {
125     int val = 1;
126     int fd = c->fd;
127 
128     if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
129         __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
130         return REDIS_ERR;
131     }
132 
133     val = interval;
134 
135 #ifdef _OSX
136     if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
137         __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
138         return REDIS_ERR;
139     }
140 #else
141 #ifndef __sun
142     val = interval;
143     if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
144         __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
145         return REDIS_ERR;
146     }
147 
148     val = interval/3;
149     if (val == 0) val = 1;
150     if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
151         __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
152         return REDIS_ERR;
153     }
154 
155     val = 3;
156     if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
157         __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
158         return REDIS_ERR;
159     }
160 #endif
161 #endif
162 
163     return REDIS_OK;
164 }
165 
redisSetTcpNoDelay(redisContext * c)166 static int redisSetTcpNoDelay(redisContext *c) {
167     int yes = 1;
168     if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
169         __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
170         redisContextCloseFd(c);
171         return REDIS_ERR;
172     }
173     return REDIS_OK;
174 }
175 
176 #define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
177 
redisContextWaitReady(redisContext * c,const struct timeval * timeout)178 static int redisContextWaitReady(redisContext *c, const struct timeval *timeout) {
179     struct pollfd   wfd[1];
180     long msec;
181 
182     msec          = -1;
183     wfd[0].fd     = c->fd;
184     wfd[0].events = POLLOUT;
185 
186     /* Only use timeout when not NULL. */
187     if (timeout != NULL) {
188         if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
189             __redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL);
190             redisContextCloseFd(c);
191             return REDIS_ERR;
192         }
193 
194         msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
195 
196         if (msec < 0 || msec > INT_MAX) {
197             msec = INT_MAX;
198         }
199     }
200 
201     if (errno == EINPROGRESS) {
202         int res;
203 
204         if ((res = poll(wfd, 1, msec)) == -1) {
205             __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
206             redisContextCloseFd(c);
207             return REDIS_ERR;
208         } else if (res == 0) {
209             errno = ETIMEDOUT;
210             __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
211             redisContextCloseFd(c);
212             return REDIS_ERR;
213         }
214 
215         if (redisCheckSocketError(c) != REDIS_OK)
216             return REDIS_ERR;
217 
218         return REDIS_OK;
219     }
220 
221     __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
222     redisContextCloseFd(c);
223     return REDIS_ERR;
224 }
225 
redisCheckSocketError(redisContext * c)226 int redisCheckSocketError(redisContext *c) {
227     int err = 0;
228     socklen_t errlen = sizeof(err);
229 
230     if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
231         __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
232         return REDIS_ERR;
233     }
234 
235     if (err) {
236         errno = err;
237         __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
238         return REDIS_ERR;
239     }
240 
241     return REDIS_OK;
242 }
243 
redisContextSetTimeout(redisContext * c,const struct timeval tv)244 int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
245     if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
246         __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
247         return REDIS_ERR;
248     }
249     if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
250         __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
251         return REDIS_ERR;
252     }
253     return REDIS_OK;
254 }
255 
_redisContextConnectTcp(redisContext * c,const char * addr,int port,const struct timeval * timeout,const char * source_addr)256 static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
257                                    const struct timeval *timeout,
258                                    const char *source_addr) {
259     int s, rv;
260     char _port[6];  /* strlen("65535"); */
261     struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
262     int blocking = (c->flags & REDIS_BLOCK);
263 
264     snprintf(_port, 6, "%d", port);
265     memset(&hints,0,sizeof(hints));
266     hints.ai_family = AF_INET;
267     hints.ai_socktype = SOCK_STREAM;
268 
269     /* Try with IPv6 if no IPv4 address was found. We do it in this order since
270      * in a Redis client you can't afford to test if you have IPv6 connectivity
271      * as this would add latency to every connect. Otherwise a more sensible
272      * route could be: Use IPv6 if both addresses are available and there is IPv6
273      * connectivity. */
274     if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
275          hints.ai_family = AF_INET6;
276          if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
277             __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
278             return REDIS_ERR;
279         }
280     }
281     for (p = servinfo; p != NULL; p = p->ai_next) {
282         if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
283             continue;
284 
285         c->fd = s;
286         if (redisSetBlocking(c,0) != REDIS_OK)
287             goto error;
288         if (source_addr) {
289             int bound = 0;
290             /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
291             if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0) {
292                 char buf[128];
293                 snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
294                 __redisSetError(c,REDIS_ERR_OTHER,buf);
295                 goto error;
296             }
297             for (b = bservinfo; b != NULL; b = b->ai_next) {
298                 if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
299                     bound = 1;
300                     break;
301                 }
302             }
303             freeaddrinfo(bservinfo);
304             if (!bound) {
305                 char buf[128];
306                 snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
307                 __redisSetError(c,REDIS_ERR_OTHER,buf);
308                 goto error;
309             }
310         }
311         if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
312             if (errno == EHOSTUNREACH) {
313                 redisContextCloseFd(c);
314                 continue;
315             } else if (errno == EINPROGRESS && !blocking) {
316                 /* This is ok. */
317             } else {
318                 if (redisContextWaitReady(c,timeout) != REDIS_OK)
319                     goto error;
320             }
321         }
322         if (blocking && redisSetBlocking(c,1) != REDIS_OK)
323             goto error;
324         if (redisSetTcpNoDelay(c) != REDIS_OK)
325             goto error;
326 
327         c->flags |= REDIS_CONNECTED;
328         rv = REDIS_OK;
329         goto end;
330     }
331     if (p == NULL) {
332         char buf[128];
333         snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
334         __redisSetError(c,REDIS_ERR_OTHER,buf);
335         goto error;
336     }
337 
338 error:
339     rv = REDIS_ERR;
340 end:
341     freeaddrinfo(servinfo);
342     return rv;  // Need to return REDIS_OK if alright
343 }
344 
redisContextConnectTcp(redisContext * c,const char * addr,int port,const struct timeval * timeout)345 int redisContextConnectTcp(redisContext *c, const char *addr, int port,
346                            const struct timeval *timeout) {
347     return _redisContextConnectTcp(c, addr, port, timeout, NULL);
348 }
349 
redisContextConnectBindTcp(redisContext * c,const char * addr,int port,const struct timeval * timeout,const char * source_addr)350 int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
351                                const struct timeval *timeout,
352                                const char *source_addr) {
353     return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
354 }
355 
redisContextConnectUnix(redisContext * c,const char * path,const struct timeval * timeout)356 int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
357     int blocking = (c->flags & REDIS_BLOCK);
358     struct sockaddr_un sa;
359 
360     if (redisCreateSocket(c,AF_LOCAL) < 0)
361         return REDIS_ERR;
362     if (redisSetBlocking(c,0) != REDIS_OK)
363         return REDIS_ERR;
364 
365     sa.sun_family = AF_LOCAL;
366     strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
367     if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
368         if (errno == EINPROGRESS && !blocking) {
369             /* This is ok. */
370         } else {
371             if (redisContextWaitReady(c,timeout) != REDIS_OK)
372                 return REDIS_ERR;
373         }
374     }
375 
376     /* Reset socket to be blocking after connect(2). */
377     if (blocking && redisSetBlocking(c,1) != REDIS_OK)
378         return REDIS_ERR;
379 
380     c->flags |= REDIS_CONNECTED;
381     return REDIS_OK;
382 }
383