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 */
51 void __redisSetError(redisContext *c, int type, const char *str);
52 
redisNetClose(redisContext * c)53 void redisNetClose(redisContext *c) {
54     if (c && c->fd != REDIS_INVALID_FD) {
55         close(c->fd);
56         c->fd = REDIS_INVALID_FD;
57     }
58 }
59 
redisNetRead(redisContext * c,char * buf,size_t bufcap)60 ssize_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 
redisNetWrite(redisContext * c)82 ssize_t redisNetWrite(redisContext *c) {
83     ssize_t nwritten = send(c->fd, c->obuf, 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 
__redisSetErrorFromErrno(redisContext * c,int type,const char * prefix)95 static 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 
redisSetReuseAddr(redisContext * c)106 static 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 
redisCreateSocket(redisContext * c,int type)116 static 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 
redisSetBlocking(redisContext * c,int blocking)131 static 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 
redisKeepAlive(redisContext * c,int interval)165 int 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 
redisSetTcpNoDelay(redisContext * c)206 int 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 
redisContextTimeoutMsec(redisContext * c,long * result)218 static 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 
redisContextWaitReady(redisContext * c,long msec)241 static 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 
redisCheckConnectDone(redisContext * c,int * completed)274 int 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 
redisCheckSocketError(redisContext * c)294 int 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 
redisContextSetTimeout(redisContext * c,const struct timeval tv)316 int 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 
redisContextUpdateConnectTimeout(redisContext * c,const struct timeval * timeout)331 int 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 
redisContextUpdateCommandTimeout(redisContext * c,const struct timeval * timeout)347 int 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 
_redisContextConnectTcp(redisContext * c,const char * addr,int port,const struct timeval * timeout,const char * source_addr)363 static 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) {
433 addrretry:
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 
524 oom:
525     __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
526 error:
527     rv = REDIS_ERR;
528 end:
529     if(servinfo) {
530         freeaddrinfo(servinfo);
531     }
532 
533     return rv;  // Need to return REDIS_OK if alright
534 }
535 
redisContextConnectTcp(redisContext * c,const char * addr,int port,const struct timeval * timeout)536 int redisContextConnectTcp(redisContext *c, const char *addr, int port,
537                            const struct timeval *timeout) {
538     return _redisContextConnectTcp(c, addr, port, timeout, NULL);
539 }
540 
redisContextConnectBindTcp(redisContext * c,const char * addr,int port,const struct timeval * timeout,const char * source_addr)541 int 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 
redisContextConnectUnix(redisContext * c,const char * path,const struct timeval * timeout)547 int 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 */
609 oom:
610     __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
611     return REDIS_ERR;
612 }
613