1/* Extracted from anet.c to work properly with Hiredis error reporting.
2 *
3 * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4 * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5 * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
6 * Jan-Erik Rediger <janerik at fnordig dot com>
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * * Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * * Neither the name of Redis nor the names of its contributors may be used
19 * to endorse or promote products derived from this software without
20 * specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include "fmacros.h"
36#include <sys/types.h>
37#include <fcntl.h>
38#include <string.h>
39#include <errno.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <limits.h>
43#include <stdlib.h>
44
45#include "net.h"
46#include "sds.h"
47#include "sockcompat.h"
48#include "win32.h"
49
50/* Defined in hiredis.c */
51void __redisSetError(redisContext *c, int type, const char *str);
52
53void redisNetClose(redisContext *c) {
54 if (c && c->fd != REDIS_INVALID_FD) {
55 close(c->fd);
56 c->fd = REDIS_INVALID_FD;
57 }
58}
59
60ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
61 ssize_t nread = recv(c->fd, buf, bufcap, 0);
62 if (nread == -1) {
63 if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
64 /* Try again later */
65 return 0;
66 } else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) {
67 /* especially in windows */
68 __redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout");
69 return -1;
70 } else {
71 __redisSetError(c, REDIS_ERR_IO, NULL);
72 return -1;
73 }
74 } else if (nread == 0) {
75 __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
76 return -1;
77 } else {
78 return nread;
79 }
80}
81
82ssize_t redisNetWrite(redisContext *c) {
83 ssize_t nwritten = send(c->fd, c->obuf, hi_sdslen(c->obuf), 0);
84 if (nwritten < 0) {
85 if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
86 /* Try again later */
87 } else {
88 __redisSetError(c, REDIS_ERR_IO, NULL);
89 return -1;
90 }
91 }
92 return nwritten;
93}
94
95static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
96 int errorno = errno; /* snprintf() may change errno */
97 char buf[128] = { 0 };
98 size_t len = 0;
99
100 if (prefix != NULL)
101 len = snprintf(buf,sizeof(buf),"%s: ",prefix);
102 strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
103 __redisSetError(c,type,buf);
104}
105
106static int redisSetReuseAddr(redisContext *c) {
107 int on = 1;
108 if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
109 __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
110 redisNetClose(c);
111 return REDIS_ERR;
112 }
113 return REDIS_OK;
114}
115
116static int redisCreateSocket(redisContext *c, int type) {
117 redisFD s;
118 if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) {
119 __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
120 return REDIS_ERR;
121 }
122 c->fd = s;
123 if (type == AF_INET) {
124 if (redisSetReuseAddr(c) == REDIS_ERR) {
125 return REDIS_ERR;
126 }
127 }
128 return REDIS_OK;
129}
130
131static int redisSetBlocking(redisContext *c, int blocking) {
132#ifndef _WIN32
133 int flags;
134
135 /* Set the socket nonblocking.
136 * Note that fcntl(2) for F_GETFL and F_SETFL can't be
137 * interrupted by a signal. */
138 if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
139 __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
140 redisNetClose(c);
141 return REDIS_ERR;
142 }
143
144 if (blocking)
145 flags &= ~O_NONBLOCK;
146 else
147 flags |= O_NONBLOCK;
148
149 if (fcntl(c->fd, F_SETFL, flags) == -1) {
150 __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
151 redisNetClose(c);
152 return REDIS_ERR;
153 }
154#else
155 u_long mode = blocking ? 0 : 1;
156 if (ioctl(c->fd, FIONBIO, &mode) == -1) {
157 __redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)");
158 redisNetClose(c);
159 return REDIS_ERR;
160 }
161#endif /* _WIN32 */
162 return REDIS_OK;
163}
164
165int redisKeepAlive(redisContext *c, int interval) {
166 int val = 1;
167 redisFD fd = c->fd;
168
169 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
170 __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
171 return REDIS_ERR;
172 }
173
174 val = interval;
175
176#if defined(__APPLE__) && defined(__MACH__)
177 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
178 __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
179 return REDIS_ERR;
180 }
181#else
182#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
183 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
184 __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
185 return REDIS_ERR;
186 }
187
188 val = interval/3;
189 if (val == 0) val = 1;
190 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
191 __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
192 return REDIS_ERR;
193 }
194
195 val = 3;
196 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
197 __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
198 return REDIS_ERR;
199 }
200#endif
201#endif
202
203 return REDIS_OK;
204}
205
206int redisSetTcpNoDelay(redisContext *c) {
207 int yes = 1;
208 if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
209 __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
210 redisNetClose(c);
211 return REDIS_ERR;
212 }
213 return REDIS_OK;
214}
215
216#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
217
218static int redisContextTimeoutMsec(redisContext *c, long *result)
219{
220 const struct timeval *timeout = c->connect_timeout;
221 long msec = -1;
222
223 /* Only use timeout when not NULL. */
224 if (timeout != NULL) {
225 if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
226 *result = msec;
227 return REDIS_ERR;
228 }
229
230 msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
231
232 if (msec < 0 || msec > INT_MAX) {
233 msec = INT_MAX;
234 }
235 }
236
237 *result = msec;
238 return REDIS_OK;
239}
240
241static int redisContextWaitReady(redisContext *c, long msec) {
242 struct pollfd wfd[1];
243
244 wfd[0].fd = c->fd;
245 wfd[0].events = POLLOUT;
246
247 if (errno == EINPROGRESS) {
248 int res;
249
250 if ((res = poll(wfd, 1, msec)) == -1) {
251 __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
252 redisNetClose(c);
253 return REDIS_ERR;
254 } else if (res == 0) {
255 errno = ETIMEDOUT;
256 __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
257 redisNetClose(c);
258 return REDIS_ERR;
259 }
260
261 if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {
262 redisCheckSocketError(c);
263 return REDIS_ERR;
264 }
265
266 return REDIS_OK;
267 }
268
269 __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
270 redisNetClose(c);
271 return REDIS_ERR;
272}
273
274int redisCheckConnectDone(redisContext *c, int *completed) {
275 int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen);
276 if (rc == 0) {
277 *completed = 1;
278 return REDIS_OK;
279 }
280 switch (errno) {
281 case EISCONN:
282 *completed = 1;
283 return REDIS_OK;
284 case EALREADY:
285 case EINPROGRESS:
286 case EWOULDBLOCK:
287 *completed = 0;
288 return REDIS_OK;
289 default:
290 return REDIS_ERR;
291 }
292}
293
294int redisCheckSocketError(redisContext *c) {
295 int err = 0, errno_saved = errno;
296 socklen_t errlen = sizeof(err);
297
298 if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
299 __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
300 return REDIS_ERR;
301 }
302
303 if (err == 0) {
304 err = errno_saved;
305 }
306
307 if (err) {
308 errno = err;
309 __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
310 return REDIS_ERR;
311 }
312
313 return REDIS_OK;
314}
315
316int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
317 const void *to_ptr = &tv;
318 size_t to_sz = sizeof(tv);
319
320 if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
321 __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
322 return REDIS_ERR;
323 }
324 if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {
325 __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
326 return REDIS_ERR;
327 }
328 return REDIS_OK;
329}
330
331int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) {
332 /* Same timeval struct, short circuit */
333 if (c->connect_timeout == timeout)
334 return REDIS_OK;
335
336 /* Allocate context timeval if we need to */
337 if (c->connect_timeout == NULL) {
338 c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout));
339 if (c->connect_timeout == NULL)
340 return REDIS_ERR;
341 }
342
343 memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout));
344 return REDIS_OK;
345}
346
347int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) {
348 /* Same timeval struct, short circuit */
349 if (c->command_timeout == timeout)
350 return REDIS_OK;
351
352 /* Allocate context timeval if we need to */
353 if (c->command_timeout == NULL) {
354 c->command_timeout = hi_malloc(sizeof(*c->command_timeout));
355 if (c->command_timeout == NULL)
356 return REDIS_ERR;
357 }
358
359 memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout));
360 return REDIS_OK;
361}
362
363static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
364 const struct timeval *timeout,
365 const char *source_addr) {
366 redisFD s;
367 int rv, n;
368 char _port[6]; /* strlen("65535"); */
369 struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
370 int blocking = (c->flags & REDIS_BLOCK);
371 int reuseaddr = (c->flags & REDIS_REUSEADDR);
372 int reuses = 0;
373 long timeout_msec = -1;
374
375 servinfo = NULL;
376 c->connection_type = REDIS_CONN_TCP;
377 c->tcp.port = port;
378
379 /* We need to take possession of the passed parameters
380 * to make them reusable for a reconnect.
381 * We also carefully check we don't free data we already own,
382 * as in the case of the reconnect method.
383 *
384 * This is a bit ugly, but atleast it works and doesn't leak memory.
385 **/
386 if (c->tcp.host != addr) {
387 hi_free(c->tcp.host);
388
389 c->tcp.host = hi_strdup(addr);
390 if (c->tcp.host == NULL)
391 goto oom;
392 }
393
394 if (timeout) {
395 if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
396 goto oom;
397 } else {
398 hi_free(c->connect_timeout);
399 c->connect_timeout = NULL;
400 }
401
402 if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
403 __redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
404 goto error;
405 }
406
407 if (source_addr == NULL) {
408 hi_free(c->tcp.source_addr);
409 c->tcp.source_addr = NULL;
410 } else if (c->tcp.source_addr != source_addr) {
411 hi_free(c->tcp.source_addr);
412 c->tcp.source_addr = hi_strdup(source_addr);
413 }
414
415 snprintf(_port, 6, "%d", port);
416 memset(&hints,0,sizeof(hints));
417 hints.ai_family = AF_INET;
418 hints.ai_socktype = SOCK_STREAM;
419
420 /* Try with IPv6 if no IPv4 address was found. We do it in this order since
421 * in a Redis client you can't afford to test if you have IPv6 connectivity
422 * as this would add latency to every connect. Otherwise a more sensible
423 * route could be: Use IPv6 if both addresses are available and there is IPv6
424 * connectivity. */
425 if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
426 hints.ai_family = AF_INET6;
427 if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
428 __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
429 return REDIS_ERR;
430 }
431 }
432 for (p = servinfo; p != NULL; p = p->ai_next) {
433addrretry:
434 if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD)
435 continue;
436
437 c->fd = s;
438 if (redisSetBlocking(c,0) != REDIS_OK)
439 goto error;
440 if (c->tcp.source_addr) {
441 int bound = 0;
442 /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
443 if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
444 char buf[128];
445 snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
446 __redisSetError(c,REDIS_ERR_OTHER,buf);
447 goto error;
448 }
449
450 if (reuseaddr) {
451 n = 1;
452 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
453 sizeof(n)) < 0) {
454 freeaddrinfo(bservinfo);
455 goto error;
456 }
457 }
458
459 for (b = bservinfo; b != NULL; b = b->ai_next) {
460 if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
461 bound = 1;
462 break;
463 }
464 }
465 freeaddrinfo(bservinfo);
466 if (!bound) {
467 char buf[128];
468 snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
469 __redisSetError(c,REDIS_ERR_OTHER,buf);
470 goto error;
471 }
472 }
473
474 /* For repeat connection */
475 hi_free(c->saddr);
476 c->saddr = hi_malloc(p->ai_addrlen);
477 if (c->saddr == NULL)
478 goto oom;
479
480 memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
481 c->addrlen = p->ai_addrlen;
482
483 if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
484 if (errno == EHOSTUNREACH) {
485 redisNetClose(c);
486 continue;
487 } else if (errno == EINPROGRESS) {
488 if (blocking) {
489 goto wait_for_ready;
490 }
491 /* This is ok.
492 * Note that even when it's in blocking mode, we unset blocking
493 * for `connect()`
494 */
495 } else if (errno == EADDRNOTAVAIL && reuseaddr) {
496 if (++reuses >= REDIS_CONNECT_RETRIES) {
497 goto error;
498 } else {
499 redisNetClose(c);
500 goto addrretry;
501 }
502 } else {
503 wait_for_ready:
504 if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
505 goto error;
506 if (redisSetTcpNoDelay(c) != REDIS_OK)
507 goto error;
508 }
509 }
510 if (blocking && redisSetBlocking(c,1) != REDIS_OK)
511 goto error;
512
513 c->flags |= REDIS_CONNECTED;
514 rv = REDIS_OK;
515 goto end;
516 }
517 if (p == NULL) {
518 char buf[128];
519 snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
520 __redisSetError(c,REDIS_ERR_OTHER,buf);
521 goto error;
522 }
523
524oom:
525 __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
526error:
527 rv = REDIS_ERR;
528end:
529 if(servinfo) {
530 freeaddrinfo(servinfo);
531 }
532
533 return rv; // Need to return REDIS_OK if alright
534}
535
536int redisContextConnectTcp(redisContext *c, const char *addr, int port,
537 const struct timeval *timeout) {
538 return _redisContextConnectTcp(c, addr, port, timeout, NULL);
539}
540
541int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
542 const struct timeval *timeout,
543 const char *source_addr) {
544 return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
545}
546
547int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
548#ifndef _WIN32
549 int blocking = (c->flags & REDIS_BLOCK);
550 struct sockaddr_un *sa;
551 long timeout_msec = -1;
552
553 if (redisCreateSocket(c,AF_UNIX) < 0)
554 return REDIS_ERR;
555 if (redisSetBlocking(c,0) != REDIS_OK)
556 return REDIS_ERR;
557
558 c->connection_type = REDIS_CONN_UNIX;
559 if (c->unix_sock.path != path) {
560 hi_free(c->unix_sock.path);
561
562 c->unix_sock.path = hi_strdup(path);
563 if (c->unix_sock.path == NULL)
564 goto oom;
565 }
566
567 if (timeout) {
568 if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
569 goto oom;
570 } else {
571 hi_free(c->connect_timeout);
572 c->connect_timeout = NULL;
573 }
574
575 if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
576 return REDIS_ERR;
577
578 /* Don't leak sockaddr if we're reconnecting */
579 if (c->saddr) hi_free(c->saddr);
580
581 sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un)));
582 if (sa == NULL)
583 goto oom;
584
585 c->addrlen = sizeof(struct sockaddr_un);
586 sa->sun_family = AF_UNIX;
587 strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);
588 if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) {
589 if (errno == EINPROGRESS && !blocking) {
590 /* This is ok. */
591 } else {
592 if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
593 return REDIS_ERR;
594 }
595 }
596
597 /* Reset socket to be blocking after connect(2). */
598 if (blocking && redisSetBlocking(c,1) != REDIS_OK)
599 return REDIS_ERR;
600
601 c->flags |= REDIS_CONNECTED;
602 return REDIS_OK;
603#else
604 /* We currently do not support Unix sockets for Windows. */
605 /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */
606 errno = EPROTONOSUPPORT;
607 return REDIS_ERR;
608#endif /* _WIN32 */
609oom:
610 __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
611 return REDIS_ERR;
612}
613