Bug Summary

File:deps/hiredis/net.c
Warning:line 425, column 10
Although the value stored to 'rv' is used in the enclosing expression, the value is never actually read from 'rv'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name net.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=none -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/llvm-10/lib/clang/10.0.0 -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-10/lib/clang/10.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wwrite-strings -Wno-missing-field-initializers -std=c99 -fconst-strings -fdebug-compilation-dir /home/netto/Desktop/redis-6.2.1/deps/hiredis -ferror-limit 19 -fmessage-length 0 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fdiagnostics-show-option -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-03-14-133648-8817-1 -x c net.c
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(*__errno_location ()).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-1) {
55 close(c->fd);
56 c->fd = REDIS_INVALID_FD-1;
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(*__errno_location ()) == EWOULDBLOCK11 && !(c->flags & REDIS_BLOCK0x1)) || (errno(*__errno_location ()) == EINTR4)) {
64 /* Try again later */
65 return 0;
66 } else if(errno(*__errno_location ()) == ETIMEDOUT110 && (c->flags & REDIS_BLOCK0x1)) {
67 /* especially in windows */
68 __redisSetError(c, REDIS_ERR_TIMEOUT6, "recv timeout");
69 return -1;
70 } else {
71 __redisSetError(c, REDIS_ERR_IO1, NULL((void*)0));
72 return -1;
73 }
74 } else if (nread == 0) {
75 __redisSetError(c, REDIS_ERR_EOF3, "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(*__errno_location ()) == EWOULDBLOCK11 && !(c->flags & REDIS_BLOCK0x1)) || (errno(*__errno_location ()) == EINTR4)) {
86 /* Try again later */
87 } else {
88 __redisSetError(c, REDIS_ERR_IO1, NULL((void*)0));
89 return -1;
90 }
91 }
92 return nwritten;
93}
94
95static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
96 int errorno = errno(*__errno_location ()); /* snprintf() may change errno */
97 char buf[128] = { 0 };
98 size_t len = 0;
99
100 if (prefix != NULL((void*)0))
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_SOCKET1, SO_REUSEADDR2, &on, sizeof(on)) == -1) {
109 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0));
110 redisNetClose(c);
111 return REDIS_ERR-1;
112 }
113 return REDIS_OK0;
114}
115
116static int redisCreateSocket(redisContext *c, int type) {
117 redisFD s;
118 if ((s = socket(type, SOCK_STREAMSOCK_STREAM, 0)) == REDIS_INVALID_FD-1) {
119 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0));
120 return REDIS_ERR-1;
121 }
122 c->fd = s;
123 if (type == AF_INET2) {
124 if (redisSetReuseAddr(c) == REDIS_ERR-1) {
125 return REDIS_ERR-1;
126 }
127 }
128 return REDIS_OK0;
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_GETFL3)) == -1) {
139 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"fcntl(F_GETFL)");
140 redisNetClose(c);
141 return REDIS_ERR-1;
142 }
143
144 if (blocking)
145 flags &= ~O_NONBLOCK04000;
146 else
147 flags |= O_NONBLOCK04000;
148
149 if (fcntl(c->fd, F_SETFL4, flags) == -1) {
150 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"fcntl(F_SETFL)");
151 redisNetClose(c);
152 return REDIS_ERR-1;
153 }
154#else
155 u_long mode = blocking ? 0 : 1;
156 if (ioctl(c->fd, FIONBIO, &mode) == -1) {
157 __redisSetErrorFromErrno(c, REDIS_ERR_IO1, "ioctl(FIONBIO)");
158 redisNetClose(c);
159 return REDIS_ERR-1;
160 }
161#endif /* _WIN32 */
162 return REDIS_OK0;
163}
164
165int redisKeepAlive(redisContext *c, int interval) {
166 int val = 1;
167 redisFD fd = c->fd;
168
169 if (setsockopt(fd, SOL_SOCKET1, SO_KEEPALIVE9, &val, sizeof(val)) == -1){
170 __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ())));
171 return REDIS_ERR-1;
172 }
173
174 val = interval;
175
176#if defined(__APPLE__) && defined(__MACH__)
177 if (setsockopt(fd, IPPROTO_TCPIPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
178 __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ())));
179 return REDIS_ERR-1;
180 }
181#else
182#if defined(__GLIBC__2) && !defined(__FreeBSD_kernel__)
183 if (setsockopt(fd, IPPROTO_TCPIPPROTO_TCP, TCP_KEEPIDLE4, &val, sizeof(val)) < 0) {
184 __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ())));
185 return REDIS_ERR-1;
186 }
187
188 val = interval/3;
189 if (val == 0) val = 1;
190 if (setsockopt(fd, IPPROTO_TCPIPPROTO_TCP, TCP_KEEPINTVL5, &val, sizeof(val)) < 0) {
191 __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ())));
192 return REDIS_ERR-1;
193 }
194
195 val = 3;
196 if (setsockopt(fd, IPPROTO_TCPIPPROTO_TCP, TCP_KEEPCNT6, &val, sizeof(val)) < 0) {
197 __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ())));
198 return REDIS_ERR-1;
199 }
200#endif
201#endif
202
203 return REDIS_OK0;
204}
205
206int redisSetTcpNoDelay(redisContext *c) {
207 int yes = 1;
208 if (setsockopt(c->fd, IPPROTO_TCPIPPROTO_TCP, TCP_NODELAY1, &yes, sizeof(yes)) == -1) {
209 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"setsockopt(TCP_NODELAY)");
210 redisNetClose(c);
211 return REDIS_ERR-1;
212 }
213 return REDIS_OK0;
214}
215
216#define __MAX_MSEC(((9223372036854775807L) - 999) / 1000) (((LONG_MAX9223372036854775807L) - 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((void*)0)) {
225 if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC(((9223372036854775807L) - 999) / 1000)) {
226 *result = msec;
227 return REDIS_ERR-1;
228 }
229
230 msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
231
232 if (msec < 0 || msec > INT_MAX2147483647) {
233 msec = INT_MAX2147483647;
234 }
235 }
236
237 *result = msec;
238 return REDIS_OK0;
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 = POLLOUT0x004;
246
247 if (errno(*__errno_location ()) == EINPROGRESS115) {
248 int res;
249
250 if ((res = poll(wfd, 1, msec)) == -1) {
251 __redisSetErrorFromErrno(c, REDIS_ERR_IO1, "poll(2)");
252 redisNetClose(c);
253 return REDIS_ERR-1;
254 } else if (res == 0) {
255 errno(*__errno_location ()) = ETIMEDOUT110;
256 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0));
257 redisNetClose(c);
258 return REDIS_ERR-1;
259 }
260
261 if (redisCheckConnectDone(c, &res) != REDIS_OK0 || res == 0) {
262 redisCheckSocketError(c);
263 return REDIS_ERR-1;
264 }
265
266 return REDIS_OK0;
267 }
268
269 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0));
270 redisNetClose(c);
271 return REDIS_ERR-1;
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_OK0;
279 }
280 switch (errno(*__errno_location ())) {
281 case EISCONN106:
282 *completed = 1;
283 return REDIS_OK0;
284 case EALREADY114:
285 case EINPROGRESS115:
286 case EWOULDBLOCK11:
287 *completed = 0;
288 return REDIS_OK0;
289 default:
290 return REDIS_ERR-1;
291 }
292}
293
294int redisCheckSocketError(redisContext *c) {
295 int err = 0, errno_saved = errno(*__errno_location ());
296 socklen_t errlen = sizeof(err);
297
298 if (getsockopt(c->fd, SOL_SOCKET1, SO_ERROR4, &err, &errlen) == -1) {
299 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"getsockopt(SO_ERROR)");
300 return REDIS_ERR-1;
301 }
302
303 if (err == 0) {
304 err = errno_saved;
305 }
306
307 if (err) {
308 errno(*__errno_location ()) = err;
309 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0));
310 return REDIS_ERR-1;
311 }
312
313 return REDIS_OK0;
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_SOCKET1,SO_RCVTIMEO20,to_ptr,to_sz) == -1) {
321 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"setsockopt(SO_RCVTIMEO)");
322 return REDIS_ERR-1;
323 }
324 if (setsockopt(c->fd,SOL_SOCKET1,SO_SNDTIMEO21,to_ptr,to_sz) == -1) {
325 __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"setsockopt(SO_SNDTIMEO)");
326 return REDIS_ERR-1;
327 }
328 return REDIS_OK0;
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_OK0;
335
336 /* Allocate context timeval if we need to */
337 if (c->connect_timeout == NULL((void*)0)) {
338 c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout));
339 if (c->connect_timeout == NULL((void*)0))
340 return REDIS_ERR-1;
341 }
342
343 memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout));
344 return REDIS_OK0;
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_OK0;
351
352 /* Allocate context timeval if we need to */
353 if (c->command_timeout == NULL((void*)0)) {
354 c->command_timeout = hi_malloc(sizeof(*c->command_timeout));
355 if (c->command_timeout == NULL((void*)0))
356 return REDIS_ERR-1;
357 }
358
359 memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout));
360 return REDIS_OK0;
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_BLOCK0x1);
371 int reuseaddr = (c->flags & REDIS_REUSEADDR0x80);
372 int reuses = 0;
373 long timeout_msec = -1;
374
375 servinfo = NULL((void*)0);
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((void*)0))
391 goto oom;
392 }
393
394 if (timeout) {
395 if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR-1)
396 goto oom;
397 } else {
398 hi_free(c->connect_timeout);
399 c->connect_timeout = NULL((void*)0);
400 }
401
402 if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK0) {
403 __redisSetError(c, REDIS_ERR_IO1, "Invalid timeout specified");
404 goto error;
405 }
406
407 if (source_addr == NULL((void*)0)) {
408 hi_free(c->tcp.source_addr);
409 c->tcp.source_addr = NULL((void*)0);
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_INET2;
418 hints.ai_socktype = SOCK_STREAMSOCK_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) {
Although the value stored to 'rv' is used in the enclosing expression, the value is never actually read from 'rv'
426 hints.ai_family = AF_INET610;
427 if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
428 __redisSetError(c,REDIS_ERR_OTHER2,gai_strerror(rv));
429 return REDIS_ERR-1;
430 }
431 }
432 for (p = servinfo; p != NULL((void*)0); p = p->ai_next) {
433addrretry:
434 if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD-1)
435 continue;
436
437 c->fd = s;
438 if (redisSetBlocking(c,0) != REDIS_OK0)
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((void*)0), &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_OTHER2,buf);
447 goto error;
448 }
449
450 if (reuseaddr) {
451 n = 1;
452 if (setsockopt(s, SOL_SOCKET1, SO_REUSEADDR2, (char*) &n,
453 sizeof(n)) < 0) {
454 freeaddrinfo(bservinfo);
455 goto error;
456 }
457 }
458
459 for (b = bservinfo; b != NULL((void*)0); 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(*__errno_location ())));
469 __redisSetError(c,REDIS_ERR_OTHER2,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((void*)0))
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(*__errno_location ()) == EHOSTUNREACH113) {
485 redisNetClose(c);
486 continue;
487 } else if (errno(*__errno_location ()) == EINPROGRESS115) {
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(*__errno_location ()) == EADDRNOTAVAIL99 && reuseaddr) {
496 if (++reuses >= REDIS_CONNECT_RETRIES10) {
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_OK0)
505 goto error;
506 if (redisSetTcpNoDelay(c) != REDIS_OK0)
507 goto error;
508 }
509 }
510 if (blocking && redisSetBlocking(c,1) != REDIS_OK0)
511 goto error;
512
513 c->flags |= REDIS_CONNECTED0x2;
514 rv = REDIS_OK0;
515 goto end;
516 }
517 if (p == NULL((void*)0)) {
518 char buf[128];
519 snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno(*__errno_location ())));
520 __redisSetError(c,REDIS_ERR_OTHER2,buf);
521 goto error;
522 }
523
524oom:
525 __redisSetError(c, REDIS_ERR_OOM5, "Out of memory");
526error:
527 rv = REDIS_ERR-1;
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((void*)0));
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_BLOCK0x1);
550 struct sockaddr_un *sa;
551 long timeout_msec = -1;
552
553 if (redisCreateSocket(c,AF_UNIX1) < 0)
554 return REDIS_ERR-1;
555 if (redisSetBlocking(c,0) != REDIS_OK0)
556 return REDIS_ERR-1;
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((void*)0))
564 goto oom;
565 }
566
567 if (timeout) {
568 if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR-1)
569 goto oom;
570 } else {
571 hi_free(c->connect_timeout);
572 c->connect_timeout = NULL((void*)0);
573 }
574
575 if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK0)
576 return REDIS_ERR-1;
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((void*)0))
583 goto oom;
584
585 c->addrlen = sizeof(struct sockaddr_un);
586 sa->sun_family = AF_UNIX1;
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(*__errno_location ()) == EINPROGRESS115 && !blocking) {
590 /* This is ok. */
591 } else {
592 if (redisContextWaitReady(c,timeout_msec) != REDIS_OK0)
593 return REDIS_ERR-1;
594 }
595 }
596
597 /* Reset socket to be blocking after connect(2). */
598 if (blocking && redisSetBlocking(c,1) != REDIS_OK0)
599 return REDIS_ERR-1;
600
601 c->flags |= REDIS_CONNECTED0x2;
602 return REDIS_OK0;
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(*__errno_location ()) = EPROTONOSUPPORT93;
607 return REDIS_ERR-1;
608#endif /* _WIN32 */
609oom:
610 __redisSetError(c, REDIS_ERR_OOM5, "Out of memory");
611 return REDIS_ERR-1;
612}