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 | |
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 | |
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 | |
82 | ssize_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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |